From 4ed70a930202b103e7e80b8dc925e0aaa4622595 Mon Sep 17 00:00:00 2001 From: Martin Sustrik Date: Wed, 29 Jul 2009 12:07:54 +0200 Subject: initial commit --- AUTHORS | 0 ChangeLog | 0 Makefile.am | 4 + NEWS | 0 README | 0 autogen.sh | 29 +++++ configure.in | 188 +++++++++++++++++++++++++++ examples/Makefile.am | 2 + examples/chat/Makefile.am | 15 +++ examples/chat/chatroom.cpp | 74 +++++++++++ examples/chat/display.cpp | 56 ++++++++ examples/chat/prompt.cpp | 61 +++++++++ include/zs.h | 206 ++++++++++++++++++++++++++++++ include/zs.hpp | 231 +++++++++++++++++++++++++++++++++ src/Makefile.am | 120 ++++++++++++++++++ src/app_thread.cpp | 221 ++++++++++++++++++++++++++++++++ src/app_thread.hpp | 95 ++++++++++++++ src/atomic.hpp | 310 +++++++++++++++++++++++++++++++++++++++++++++ src/atomic_bitmap.hpp | 286 +++++++++++++++++++++++++++++++++++++++++ src/atomic_counter.hpp | 197 ++++++++++++++++++++++++++++ src/atomic_ptr.hpp | 189 +++++++++++++++++++++++++++ src/command.hpp | 98 ++++++++++++++ src/config.hpp | 71 +++++++++++ src/connecter.cpp | 189 +++++++++++++++++++++++++++ src/connecter.hpp | 99 +++++++++++++++ src/data_distributor.cpp | 155 +++++++++++++++++++++++ src/data_distributor.hpp | 70 ++++++++++ src/decoder.hpp | 101 +++++++++++++++ src/devpoll.cpp | 224 ++++++++++++++++++++++++++++++++ src/devpoll.hpp | 110 ++++++++++++++++ src/dispatcher.cpp | 266 ++++++++++++++++++++++++++++++++++++++ src/dispatcher.hpp | 170 +++++++++++++++++++++++++ src/dummy_aggregator.cpp | 111 ++++++++++++++++ src/dummy_aggregator.hpp | 73 +++++++++++ src/dummy_distributor.cpp | 85 +++++++++++++ src/dummy_distributor.hpp | 68 ++++++++++ src/encoder.hpp | 108 ++++++++++++++++ src/epoll.cpp | 214 +++++++++++++++++++++++++++++++ src/epoll.hpp | 107 ++++++++++++++++ src/err.cpp | 146 +++++++++++++++++++++ src/err.hpp | 90 +++++++++++++ src/fair_aggregator.cpp | 143 +++++++++++++++++++++ src/fair_aggregator.hpp | 77 +++++++++++ src/fd.hpp | 44 +++++++ src/fd_signaler.cpp | 278 ++++++++++++++++++++++++++++++++++++++++ src/fd_signaler.hpp | 92 ++++++++++++++ src/i_api.hpp | 39 ++++++ src/i_demux.hpp | 56 ++++++++ src/i_engine.hpp | 53 ++++++++ src/i_mux.hpp | 59 +++++++++ src/i_poll_events.hpp | 45 +++++++ src/i_poller.hpp | 89 +++++++++++++ src/i_session.hpp | 37 ++++++ src/i_signaler.hpp | 38 ++++++ src/i_thread.hpp | 38 ++++++ src/io_object.cpp | 37 ++++++ src/io_object.hpp | 51 ++++++++ src/io_thread.cpp | 177 ++++++++++++++++++++++++++ src/io_thread.hpp | 99 +++++++++++++++ src/ip.cpp | 310 +++++++++++++++++++++++++++++++++++++++++++++ src/ip.hpp | 47 +++++++ src/kqueue.cpp | 214 +++++++++++++++++++++++++++++++ src/kqueue.hpp | 112 ++++++++++++++++ src/listener.cpp | 170 +++++++++++++++++++++++++ src/listener.hpp | 110 ++++++++++++++++ src/load_balancer.cpp | 130 +++++++++++++++++++ src/load_balancer.hpp | 73 +++++++++++ src/msg.hpp | 49 +++++++ src/mutex.hpp | 116 +++++++++++++++++ src/object.cpp | 294 ++++++++++++++++++++++++++++++++++++++++++ src/object.hpp | 105 +++++++++++++++ src/p2p.cpp | 29 +++++ src/p2p.hpp | 42 ++++++ src/pipe.cpp | 47 +++++++ src/pipe.hpp | 57 +++++++++ src/pipe_reader.cpp | 118 +++++++++++++++++ src/pipe_reader.hpp | 89 +++++++++++++ src/pipe_writer.cpp | 120 ++++++++++++++++++ src/pipe_writer.hpp | 88 +++++++++++++ src/platform.hpp.in | 210 ++++++++++++++++++++++++++++++ src/poll.cpp | 205 ++++++++++++++++++++++++++++++ src/poll.hpp | 112 ++++++++++++++++ src/pub.cpp | 38 ++++++ src/pub.hpp | 45 +++++++ src/rep.cpp | 29 +++++ src/rep.hpp | 42 ++++++ src/req.cpp | 29 +++++ src/req.hpp | 42 ++++++ src/safe_object.cpp | 76 +++++++++++ src/safe_object.hpp | 68 ++++++++++ src/select.cpp | 236 ++++++++++++++++++++++++++++++++++ src/select.hpp | 122 ++++++++++++++++++ src/session.cpp | 273 +++++++++++++++++++++++++++++++++++++++ src/session.hpp | 107 ++++++++++++++++ src/session_stub.cpp | 110 ++++++++++++++++ src/session_stub.hpp | 83 ++++++++++++ src/simple_semaphore.hpp | 188 +++++++++++++++++++++++++++ src/socket_base.cpp | 267 ++++++++++++++++++++++++++++++++++++++ src/socket_base.hpp | 96 ++++++++++++++ src/stdint.hpp | 70 ++++++++++ src/sub.cpp | 45 +++++++ src/sub.hpp | 46 +++++++ src/tcp_connecter.cpp | 138 ++++++++++++++++++++ src/tcp_connecter.hpp | 65 ++++++++++ src/tcp_listener.cpp | 165 ++++++++++++++++++++++++ src/tcp_listener.hpp | 65 ++++++++++ src/tcp_socket.cpp | 116 +++++++++++++++++ src/tcp_socket.hpp | 70 ++++++++++ src/thread.cpp | 88 +++++++++++++ src/thread.hpp | 77 +++++++++++ src/uuid.cpp | 136 ++++++++++++++++++++ src/uuid.hpp | 82 ++++++++++++ src/windows.hpp | 56 ++++++++ src/wire.hpp | 98 ++++++++++++++ src/ypipe.hpp | 209 ++++++++++++++++++++++++++++++ src/ypollset.cpp | 56 ++++++++ src/ypollset.hpp | 74 +++++++++++ src/yqueue.hpp | 138 ++++++++++++++++++++ src/zmq_decoder.cpp | 78 ++++++++++++ src/zmq_decoder.hpp | 57 +++++++++ src/zmq_encoder.cpp | 75 +++++++++++ src/zmq_encoder.hpp | 54 ++++++++ src/zmq_tcp_engine.cpp | 185 +++++++++++++++++++++++++++ src/zmq_tcp_engine.hpp | 92 ++++++++++++++ src/zs.cpp | 222 ++++++++++++++++++++++++++++++++ 125 files changed, 13546 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 examples/Makefile.am create mode 100644 examples/chat/Makefile.am create mode 100644 examples/chat/chatroom.cpp create mode 100644 examples/chat/display.cpp create mode 100644 examples/chat/prompt.cpp create mode 100644 include/zs.h create mode 100644 include/zs.hpp create mode 100644 src/Makefile.am create mode 100644 src/app_thread.cpp create mode 100644 src/app_thread.hpp create mode 100644 src/atomic.hpp create mode 100644 src/atomic_bitmap.hpp create mode 100644 src/atomic_counter.hpp create mode 100644 src/atomic_ptr.hpp create mode 100644 src/command.hpp create mode 100644 src/config.hpp create mode 100644 src/connecter.cpp create mode 100644 src/connecter.hpp create mode 100644 src/data_distributor.cpp create mode 100644 src/data_distributor.hpp create mode 100644 src/decoder.hpp create mode 100644 src/devpoll.cpp create mode 100644 src/devpoll.hpp create mode 100644 src/dispatcher.cpp create mode 100644 src/dispatcher.hpp create mode 100644 src/dummy_aggregator.cpp create mode 100644 src/dummy_aggregator.hpp create mode 100644 src/dummy_distributor.cpp create mode 100644 src/dummy_distributor.hpp create mode 100644 src/encoder.hpp create mode 100644 src/epoll.cpp create mode 100644 src/epoll.hpp create mode 100644 src/err.cpp create mode 100644 src/err.hpp create mode 100644 src/fair_aggregator.cpp create mode 100644 src/fair_aggregator.hpp create mode 100644 src/fd.hpp create mode 100644 src/fd_signaler.cpp create mode 100644 src/fd_signaler.hpp create mode 100644 src/i_api.hpp create mode 100644 src/i_demux.hpp create mode 100644 src/i_engine.hpp create mode 100644 src/i_mux.hpp create mode 100644 src/i_poll_events.hpp create mode 100644 src/i_poller.hpp create mode 100644 src/i_session.hpp create mode 100644 src/i_signaler.hpp create mode 100644 src/i_thread.hpp create mode 100644 src/io_object.cpp create mode 100644 src/io_object.hpp create mode 100644 src/io_thread.cpp create mode 100644 src/io_thread.hpp create mode 100644 src/ip.cpp create mode 100644 src/ip.hpp create mode 100644 src/kqueue.cpp create mode 100644 src/kqueue.hpp create mode 100644 src/listener.cpp create mode 100644 src/listener.hpp create mode 100644 src/load_balancer.cpp create mode 100644 src/load_balancer.hpp create mode 100644 src/msg.hpp create mode 100644 src/mutex.hpp create mode 100644 src/object.cpp create mode 100644 src/object.hpp create mode 100644 src/p2p.cpp create mode 100644 src/p2p.hpp create mode 100644 src/pipe.cpp create mode 100644 src/pipe.hpp create mode 100644 src/pipe_reader.cpp create mode 100644 src/pipe_reader.hpp create mode 100644 src/pipe_writer.cpp create mode 100644 src/pipe_writer.hpp create mode 100644 src/platform.hpp.in create mode 100644 src/poll.cpp create mode 100644 src/poll.hpp create mode 100644 src/pub.cpp create mode 100644 src/pub.hpp create mode 100644 src/rep.cpp create mode 100644 src/rep.hpp create mode 100644 src/req.cpp create mode 100644 src/req.hpp create mode 100644 src/safe_object.cpp create mode 100644 src/safe_object.hpp create mode 100644 src/select.cpp create mode 100644 src/select.hpp create mode 100644 src/session.cpp create mode 100644 src/session.hpp create mode 100644 src/session_stub.cpp create mode 100644 src/session_stub.hpp create mode 100644 src/simple_semaphore.hpp create mode 100644 src/socket_base.cpp create mode 100644 src/socket_base.hpp create mode 100644 src/stdint.hpp create mode 100644 src/sub.cpp create mode 100644 src/sub.hpp create mode 100644 src/tcp_connecter.cpp create mode 100644 src/tcp_connecter.hpp create mode 100644 src/tcp_listener.cpp create mode 100644 src/tcp_listener.hpp create mode 100644 src/tcp_socket.cpp create mode 100644 src/tcp_socket.hpp create mode 100644 src/thread.cpp create mode 100644 src/thread.hpp create mode 100644 src/uuid.cpp create mode 100644 src/uuid.hpp create mode 100644 src/windows.hpp create mode 100644 src/wire.hpp create mode 100644 src/ypipe.hpp create mode 100644 src/ypollset.cpp create mode 100644 src/ypollset.hpp create mode 100644 src/yqueue.hpp create mode 100644 src/zmq_decoder.cpp create mode 100644 src/zmq_decoder.hpp create mode 100644 src/zmq_encoder.cpp create mode 100644 src/zmq_encoder.hpp create mode 100644 src/zmq_tcp_engine.cpp create mode 100644 src/zmq_tcp_engine.hpp create mode 100644 src/zs.cpp diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..cc77307 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +include_HEADERS = include/zs.h include/zs.hpp + +SUBDIRS = src examples +DIST_SUBDIRS = src examples diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..19f2174 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# Copyright (c) 2007 FastMQ Inc. +# +# This file is part of 0MQ. +# +# 0MQ is free software; you can redistribute it and/or modify +# it under the terms of the 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 +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Script to generate all required files from fresh svn checkout. + + + +autoreconf --install --force --verbose -I config + +if [ $? -ne 0 ]; then + echo + echo "Could not run autoreconf, check autotools installation." + echo +fi diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..e75ab11 --- /dev/null +++ b/configure.in @@ -0,0 +1,188 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. +AC_PREREQ(2.61) +AC_INIT([zsock],[dev]) +AC_CONFIG_AUX_DIR(config) +AM_CONFIG_HEADER(src/platform.hpp) +AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) + +AM_PROG_CC_C_O + +# Checks for programs. +AC_PROG_CXX +AC_PROG_LIBTOOL + +# Checks for libraries. +AC_CHECK_LIB(pthread, pthread_create) + +# Host speciffic checks +AC_CANONICAL_HOST + +case "${host_os}" in + *linux*) + AC_DEFINE(ZS_HAVE_LINUX, 1, [Have Linux OS]) + CPPFLAGS="-D_REENTRANT $CPPFLAGS" + sed < libtool > libtool-2 \ + 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" "/' + mv libtool-2 libtool + chmod 755 libtool + AC_CHECK_LIB(uuid, uuid_generate) + ;; + *solaris*) + AC_DEFINE(ZS_HAVE_SOLARIS, 1, [Have Solaris OS]) + AC_CHECK_LIB(socket, main) + AC_CHECK_LIB(nsl, main) + AC_CHECK_LIB(rt, main) + CPPFLAGS="-D_REENTRANT -D_PTHREADS $CPPFLAGS" + AC_MSG_CHECKING([wheter atomic operations can be used]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#include ]], + [[uint32_t value; + atomic_cas_32 (&value, 0, 0); + return 0;]])], + [solaris_has_atomic=yes], + [solaris_has_atomic=no]) + AC_MSG_RESULT([$solaris_has_atomic]) + # Solaris 8 does not have atomic operations exported to user space. + if test "x$solaris_has_atomic" = "xno"; then + AC_DEFINE(ZS_FORCE_MUTEXES, 1, [Force to use mutexes]) + fi + ;; + *freebsd*) + AC_DEFINE(ZS_HAVE_FREEBSD, 1, [Have FreeBSD OS]) + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + LIBS="-pthread" + ;; + *darwin*) + AC_DEFINE(ZS_HAVE_OSX, 1, [Have DarwinOSX OS]) + LIBS="-pthread" + ZS_EXTRA_CXXFLAGS+="-Wno-uninitialized" + ;; + *openbsd*) + AC_DEFINE(ZS_HAVE_OPENBSD, 1, [Have OpenBSD OS]) + CPPFLAGS="-pthread $CPPFLAGS" + LIBS="-pthread" + ;; + *nto-qnx*) + AC_DEFINE(ZS_HAVE_QNXNTO, 1, [Have QNX Neutrino OS]) + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + AC_CHECK_LIB(socket,main) + ;; + *aix*) + AC_DEFINE(ZS_HAVE_AIX, 1, [Have AIX OS]) + if test "x$GXX" = "xyes"; then + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + fi + ;; + *hpux*) + AC_DEFINE(ZS_HAVE_HPUX, 1, [Have HPUX OS]) + if test "x$GXX" = "xyes"; then + CPPFLAGS="-D_THREAD_SAFE $CPPFLAGS" + fi + AC_CHECK_LIB(rt, main) + sed < libtool > libtool-2 \ + 's/^hardcode_libdir_flag_spec.*$'/'hardcode_libdir_flag_spec=" "/' + mv libtool-2 libtool + chmod 755 libtool + ;; + *mingw32*) + AC_DEFINE(ZS_HAVE_WINDOWS, 1, [Have Windows OS]) + AC_DEFINE(ZS_HAVE_MINGW32, 1, [Have MinGW32]) + AC_CHECK_HEADERS(windows.h) + LIBS="-lwsock32 -lws2_32 -no-undefined" + CFLAGS="-std=c99" + install_man="no" + ;; + *) + AC_MSG_ERROR([Not supported os: $host.]) + ;; +esac + +# Check if we are running at sparc harware +AC_MSG_CHECKING([wheter __sparc__ is defined]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#if defined __sparc__ + //OK we are on sparc + #else + error: we are not on sparc + #endif + ]])], + [sparc=yes], + [sparc=no]) + +AC_MSG_RESULT([$sparc]) + +if test "x$sparc" = "xyes"; then + CPPFLAGS="$CPPFLAGS -mcpu=v9" +fi + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(errno.h arpa/inet.h netinet/tcp.h netinet/in.h stddef.h \ +stdlib.h string.h sys/socket.h sys/time.h unistd.h limits.h) + +# Check if we have eventfd.h header file. +AC_CHECK_HEADERS(sys/eventfd.h, [AC_DEFINE(ZS_HAVE_EVENTFD, 1, [Have eventfd extension.])]) + +# Check if we have ifaddrs.h header file. +AC_CHECK_HEADERS(ifaddrs.h, [AC_DEFINE(ZS_HAVE_IFADDRS, 1, [Have ifaddrs.h header.])]) + +# Use c++ in subsequent tests +AC_LANG(C++) + +# Optional stuff +AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no) + +if test "x$have_pkg_config" != "xno"; then + # First instance of PKG_CHECK_ has to be executed + PKG_CHECK_EXISTS([dummy_pkg], [], []) +fi + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_HEADER_TIME +AC_TYPE_UINT32_T +AC_C_VOLATILE + +# Substs +stdint="0" +if test "x$HAVE_STDINT_H" = "xyes"; then + stdint="1" +fi + +inttypes="0" +if test "x$HAVE_INTTYPES_H" = "xyes"; then + inttypes="1" +fi + +AC_SUBST(stdint) +AC_SUBST(inttypes) + +# Subst ZS_EXTRA_CXXFLAGS +AC_SUBST(ZS_EXTRA_CXXFLAGS) + + +# Checks for library functions. +AC_FUNC_MALLOC +AC_TYPE_SIGNAL +AC_CHECK_FUNCS(perror gettimeofday memset socket getifaddrs freeifaddrs) + +AC_OUTPUT(Makefile src/Makefile examples/Makefile examples/chat/Makefile) + +AC_MSG_RESULT([]) +AC_MSG_RESULT([ ******************************************************** ]) +AC_MSG_RESULT([ 0SOCKETS ]) +AC_MSG_RESULT([ ******************************************************** ]) +AC_MSG_RESULT([ This software is distributed under the terms and ]) +AC_MSG_RESULT([ conditions of the LESSER GNU GENERAL PUBLIC LICENSE. ]) +AC_MSG_RESULT([ See the file COPYING and COPYING.LESSER for the full ]) +AC_MSG_RESULT([ license text. ]) +AC_MSG_RESULT([ ******************************************************** ]) +AC_MSG_RESULT([]) +AC_MSG_RESULT([ zsock install dir: $prefix]) +AC_MSG_RESULT([]) + diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..5ab090f --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,2 @@ +SUBDIRS = chat +DIST_SUBDIRS = chat diff --git a/examples/chat/Makefile.am b/examples/chat/Makefile.am new file mode 100644 index 0000000..afdb827 --- /dev/null +++ b/examples/chat/Makefile.am @@ -0,0 +1,15 @@ +INCLUDES = -I$(top_builddir) -I$(top_builddir)/include + +noinst_PROGRAMS = chatroom display prompt + +chatroom_SOURCES = chatroom.cpp +chatroom_LDADD = $(top_builddir)/src/libzs.la +chatroom_CXXFLAGS = -Wall -pedantic -Werror + +display_SOURCES = display.cpp +display_LDADD = $(top_builddir)/src/libzs.la +display_CXXFLAGS = -Wall -pedantic -Werror + +prompt_SOURCES = prompt.cpp +prompt_LDADD = $(top_builddir)/src/libzs.la +prompt_CXXFLAGS = -Wall -pedantic -Werror diff --git a/examples/chat/chatroom.cpp b/examples/chat/chatroom.cpp new file mode 100644 index 0000000..f2240ab --- /dev/null +++ b/examples/chat/chatroom.cpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify + it under the terms of the 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 + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +using namespace std; + +#include + +int main (int argc, const char *argv []) +{ + // Check the command line syntax + if (argc != 3) { + cerr << "usage: chatroom " << endl; + return 1; + } + + // Retrieve command line arguments + const char *in_interface = argv [1]; + const char *out_interface = argv [2]; + + // Initialise 0MQ infrastructure + zs::context_t ctx (1, 1); + + // Create two sockets. One for receiving messages from 'propmt' + // applications, one for sending messages to 'display' applications + zs::socket_t in_socket (ctx, ZS_SUB); + in_socket.bind (in_interface); + zs::socket_t out_socket (ctx, ZS_PUB); + out_socket.bind (out_interface); + + while (true) { + + // Get a message + zs::message_t in_message; + in_socket.recv (&in_message); + + // Get the current time. Replace the newline character at the end + // by space character. + char timebuf [256]; + time_t current_time; + time (¤t_time); + snprintf (timebuf, 256, "%s", ctime (¤t_time)); + timebuf [strlen (timebuf) - 1] = ' '; + + // Create and fill in the message + zs::message_t out_message (strlen (timebuf) + in_message.size ()); + char *data = (char*) out_message.data (); + memcpy (data, timebuf, strlen (timebuf)); + data += strlen (timebuf); + memcpy (data, in_message.data (), in_message.size ()); + + // Send the message + out_socket.send (out_message); + } +} diff --git a/examples/chat/display.cpp b/examples/chat/display.cpp new file mode 100644 index 0000000..ceb096f --- /dev/null +++ b/examples/chat/display.cpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify + it under the terms of the 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 + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +using namespace std; + +#include + +int main (int argc, const char *argv []) +{ + // Check the command line syntax. + if (argc != 2) { + cerr << "usage: display " << endl; + return 1; + } + + // Retrieve command line arguments + const char *chatroom_out_address = argv [1]; + + // Initialise 0MQ infrastructure, connect to the chatroom and ask for all + // messages and gap notifications. + zs::context_t ctx (1, 1); + zs::socket_t s (ctx, ZS_SUB); + s.connect (chatroom_out_address); + s.subscribe ("*"); + + while (true) { + + // Get a message and print it to the console. + zs::message_t message; + s.recv (&message); + if (message.type () == zs::message_gap) + cout << "Problems connecting to the chatroom..." << endl; + else + cout << (char*) message.data () << flush; + } +} diff --git a/examples/chat/prompt.cpp b/examples/chat/prompt.cpp new file mode 100644 index 0000000..461e7b8 --- /dev/null +++ b/examples/chat/prompt.cpp @@ -0,0 +1,61 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + This file is part of 0MQ. + + 0MQ is free software; you can redistribute it and/or modify + it under the terms of the 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 + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include + +using namespace std; + +#include + +int main (int argc, const char *argv []) +{ + // Check the command line syntax. + if (argc != 3) { + cerr << "usage: prompt " << endl; + return 1; + } + + // Retrieve command line arguments + const char *chatroom_in_address = argv [1]; + const char *user_name = argv [2]; + + // Initialise 0MQ infrastructure and connect to the chatroom. + zs::context_t ctx (1, 1); + zs::socket_t s (ctx, ZS_PUB); + s.connect (chatroom_in_address); + + while (true) { + + // Allow user to input the message text. Prepend it by user name. + char textbuf [1024]; + char *rcc = fgets (textbuf, sizeof (textbuf), stdin); + assert (rcc); + string text (user_name); + text = text + ": " + textbuf; + + // Create the message (terminating zero is part of the message) + zs::message_t message (text.size () + 1); + memcpy (message.data (), text.c_str (), text.size () + 1); + + // Send the message + s.send (message); + } +} diff --git a/include/zs.h b/include/zs.h new file mode 100644 index 0000000..cae1a17 --- /dev/null +++ b/include/zs.h @@ -0,0 +1,206 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZSOCKETS_H_INCLUDED__ +#define __ZSOCKETS_H_INCLUDED__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#if defined MSC_VER && defined ZS_BUILDING_LIBZS +#define ZS_EXPORT __declspec(dllexport) +#else +#define ZS_EXPORT +#endif + +// Maximal size of "Very Small Message". VSMs are passed by value +// to avoid excessive memory allocation/deallocation. +#define ZS_MAX_VSM_SIZE 30 + +// Message & notification types. +#define ZS_GAP 1 +#define ZS_DELIMITER 31 +#define ZS_VSM 32 + +// The operation should be performed in non-blocking mode. I.e. if it cannot +// be processed immediately, error should be returned with errno set to EAGAIN. +#define ZS_NOBLOCK 1 + +// zs_send should not flush the message downstream immediately. Instead, it +// should batch ZS_NOFLUSH messages and send them downstream only when zs_flush +// is invoked. This is an optimisation for cases where several messages are +// sent in a single business transaction. However, the effect is measurable +// only in extremely high-perf scenarios (million messages a second or so). +// If that's not your case, use standard flushing send instead. See exchange +// example for illustration of ZS_NOFLUSH functionality. +#define ZS_NOFLUSH 2 + +// Socket to communicate with a single peer. Allows for a singe connect or a +// single accept. There's no message routing or message filtering involved. +#define ZS_P2P 0 + +// Socket to distribute data. Recv fuction is not implemeted for this socket +// type. Messages are distributed in fanout fashion to all peers. +#define ZS_PUB 1 + +// Socket to subscribe to distributed data. Send function is not implemented +// for this socket type. However, subscribe function can be used to modify the +// message filter. +#define ZS_SUB 2 + +// Socket to send requests on and receive replies from. Requests are +// load-balanced among all the peers. This socket type doesn't allow for more +// recv's that there were send's. +#define ZS_REQ 3 + +// Socket to receive requests from and send replies to. This socket type allows +// only an alternated sequence of recv's and send's. Each send is routed to +// the peer that the previous recv delivered message from. +#define ZS_REP 4 + +// Prototype for the message body deallocation functions. +// It is deliberately defined in the way to comply with standard C free. +typedef void (zs_free_fn) (void *data); + +// A message. If 'shared' is true, message content pointed to by 'content' +// is shared, i.e. reference counting is used to manage its lifetime +// rather than straighforward malloc/free. struct zs_msg_content is +// not declared in the API. +struct zs_msg +{ + struct zs_msg_content *content; + unsigned char shared; + uint16_t vsm_size; + unsigned char vsm_data [ZS_MAX_VSM_SIZE]; +}; + +// TODO: Different options... +struct zs_opts +{ + uint64_t hwm; + uint64_t lwm; + uint64_t swap; + uint64_t mask; + uint64_t taskset; + const char *identity; + const char *args; +}; + +// Initialise an empty message (zero bytes long). +ZS_EXPORT int zs_msg_init (zs_msg *msg); + +// Initialise a message 'size' bytes long. +// +// Errors: ENOMEM - the size is too large to allocate. +ZS_EXPORT int zs_msg_init_size (zs_msg *msg, size_t size); + +// Initialise a message from an existing buffer. Message isn't copied, +// instead 0SOCKETS infrastructure take ownership of the buffer and call +// deallocation functio (ffn) once it's not needed anymore. +ZS_EXPORT int zs_msg_init_data (zs_msg *msg, void *data, size_t size, + zs_free_fn *ffn); + +// Deallocate the message. +ZS_EXPORT int zs_msg_close (zs_msg *msg); + +// Move the content of the message from 'src' to 'dest'. The content isn't +// copied, just moved. 'src' is an empty message after the call. Original +// content of 'dest' message is deallocated. +ZS_EXPORT int zs_msg_move (zs_msg *dest, zs_msg *src); + +// Copy the 'src' message to 'dest'. The content isn't copied, instead +// reference count is increased. Don't modify the message data after the +// call as they are shared between two messages. Original content of 'dest' +// message is deallocated. +ZS_EXPORT int zs_msg_copy (zs_msg *dest, zs_msg *src); + +// Returns pointer to message data. +ZS_EXPORT void *zs_msg_data (zs_msg *msg); + +// Return size of message data (in bytes). +ZS_EXPORT size_t zs_msg_size (zs_msg *msg); + +// Returns type of the message. +ZS_EXPORT int zs_msg_type (zs_msg *msg); + +// Initialise 0SOCKETS context. 'app_threads' specifies maximal number +// of application threads that can have open sockets at the same time. +// 'io_threads' specifies the size of thread pool to handle I/O operations. +// +// Errors: EINVAL - one of the arguments is less than zero or there are no +// threads declared at all. +ZS_EXPORT void *zs_init (int app_threads, int io_threads); + +// Deinitialise 0SOCKETS context including all the open sockets. Closing +// sockets after zs_term has been called will result in undefined behaviour. +ZS_EXPORT int zs_term (void *context); + +// Open a socket. +// +// Errors: EINVAL - invalid socket type. +// EMFILE - the number of application threads entitled to hold open +// sockets at the same time was exceeded. +ZS_EXPORT void *zs_socket (void *context, int type); + +// Close the socket. +ZS_EXPORT int zs_close (void *s); + +// Bind the socket to a particular address. +ZS_EXPORT int zs_bind (void *s, const char *addr, zs_opts *opts); + +// Connect the socket to a particular address. +ZS_EXPORT int zs_connect (void *s, const char *addr, zs_opts *opts); + +// Subscribe for the subset of messages identified by 'criteria' argument. +ZS_EXPORT int zs_subscribe (void *s, const char *criteria); + +// Send the message 'msg' to the socket 's'. 'flags' argument can be +// combination of following values: +// ZS_NOBLOCK - if message cannot be sent, return immediately. +// ZS_NOFLUSH - message won't be sent immediately. It'll be sent with either +// subsequent flushing send or explicit call to zs_flush function. +// +// Errors: EAGAIN - message cannot be sent at the moment (applies only to +// non-blocking send). +// ENOTSUP - function isn't supported by particular socket type. +ZS_EXPORT int zs_send (void *s, zs_msg *msg, int flags); + +// Flush the messages that were send using ZS_NOFLUSH flag down the stream. +// +// Errors: ENOTSUP - function isn't supported by particular socket type. +ZS_EXPORT int zs_flush (void *s); + +// Send a message from the socket 's'. 'flags' argument can be combination +// of following values: +// ZS_NOBLOCK - if message cannot be received, return immediately. +// +// Errors: EAGAIN - message cannot be received at the moment (applies only to +// non-blocking receive). +// ENOTSUP - function isn't supported by particular socket type. +ZS_EXPORT int zs_recv (void *s, zs_msg *msg, int flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/zs.hpp b/include/zs.hpp new file mode 100644 index 0000000..d0f607f --- /dev/null +++ b/include/zs.hpp @@ -0,0 +1,231 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZSOCKETS_HPP_INCLUDED__ +#define __ZSOCKETS_HPP_INCLUDED__ + +#include "zs.h" + +#include + +namespace zs +{ + + typedef zs_free_fn free_fn; + + enum message_type_t + { + message_data = 1 << 0, + message_gap = 1 << ZS_GAP, + message_delimiter = 1 << ZS_DELIMITER + }; + + // A message. Caution: Don't change the body of the message once you've + // copied it - the behaviour is undefined. Don't change the body of the + // received message either - other threads may be accessing it in parallel. + + class message_t : private zs_msg + { + friend class socket_t; + + public: + + // Creates message size_ bytes long. + inline message_t (size_t size_ = 0) + { + int rc = zs_msg_init_size (this, size_); + assert (rc == 0); + } + + // Creates message from the supplied buffer. 0MQ takes care of + // deallocating the buffer once it is not needed. The deallocation + // function is supplied in ffn_ parameter. If ffn_ is NULL, no + // deallocation happens - this is useful for sending static buffers. + inline message_t (void *data_, size_t size_, + free_fn *ffn_) + { + int rc = zs_msg_init_data (this, data_, size_, ffn_); + assert (rc == 0); + } + + // Destroys the message. + inline ~message_t () + { + int rc = zs_msg_close (this); + assert (rc == 0); + } + + // Destroys old content of the message and allocates buffer for the + // new message body. Having this as a separate function allows user + // to reuse once-allocated message for multiple times. + inline void rebuild (size_t size_) + { + int rc = zs_msg_close (this); + assert (rc == 0); + rc = zs_msg_init_size (this, size_); + assert (rc == 0); + } + + // Same as above, however, the message is rebuilt from the supplied + // buffer. See appropriate constructor for discussion of buffer + // deallocation mechanism. + inline void rebuild (void *data_, size_t size_, free_fn *ffn_) + { + int rc = zs_msg_close (this); + assert (rc == 0); + rc = zs_msg_init_data (this, data_, size_, ffn_); + assert (rc == 0); + } + + // Moves the message content from one message to the another. If the + // destination message have contained data prior to the operation + // these get deallocated. The source message will contain 0 bytes + // of data after the operation. + inline void move_to (message_t *msg_) + { + int rc = zs_msg_move (this, (zs_msg*) msg_); + assert (rc == 0); + } + + // Copies the message content from one message to the another. If the + // destination message have contained data prior to the operation + // these get deallocated. + inline void copy_to (message_t *msg_) + { + int rc = zs_msg_copy (this, (zs_msg*) msg_); + assert (rc == 0); + } + + // Returns message type. + inline message_type_t type () + { + return (message_type_t) (1 << zs_msg_type (this)); + } + + // Returns pointer to message's data buffer. + inline void *data () + { + return zs_msg_data (this); + } + + // Returns the size of message data buffer. + inline size_t size () + { + return zs_msg_size (this); + } + + private: + + // Disable implicit message copying, so that users won't use shared + // messages (less efficient) without being aware of the fact. + message_t (const message_t&); + void operator = (const message_t&); + }; + + class context_t + { + friend class socket_t; + + public: + + inline context_t (int app_threads_, int io_threads_) + { + ptr = zs_init (app_threads_, io_threads_); + assert (ptr); + } + + inline ~context_t () + { + int rc = zs_term (ptr); + assert (rc == 0); + } + + private: + + void *ptr; + + // Disable copying. + context_t (const context_t&); + void operator = (const context_t&); + }; + + class socket_t + { + public: + + inline socket_t (context_t &context_, int type_ = 0) + { + ptr = zs_socket (context_.ptr, type_); + assert (ptr); + } + + inline ~socket_t () + { + int rc = zs_close (ptr); + assert (rc == 0); + } + + inline void bind (const char *addr_, zs_opts *opts_ = NULL) + { + int rc = zs_bind (ptr, addr_, opts_); + assert (rc == 0); + } + + inline void connect (const char *addr_, zs_opts *opts_ = NULL) + { + int rc = zs_connect (ptr, addr_, opts_); + assert (rc == 0); + } + + inline void subscribe (const char *criteria_) + { + int rc = zs_subscribe (ptr, criteria_); + assert (rc == 0); + } + + inline void send (message_t &msg_, int flags_ = 0) + { + int rc = zs_send (ptr, &msg_, flags_); + assert (rc == 0); + } + + inline void flush () + { + int rc = zs_flush (ptr); + assert (rc == 0); + } + + inline void recv (message_t *msg_, int flags_ = 0) + { + int rc = zs_recv (ptr, msg_, flags_); + assert (rc == 0); + } + + private: + + void *ptr; + + // Disable copying. + socket_t (const socket_t&); + void operator = (const socket_t&); + }; + +} + +#endif diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..bb648ec --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,120 @@ +lib_LTLIBRARIES = libzs.la + +libzs_la_SOURCES = \ + app_thread.hpp \ + atomic_bitmap.hpp \ + atomic_counter.hpp \ + atomic_ptr.hpp \ + command.hpp \ + config.hpp \ + connecter.hpp \ + data_distributor.hpp \ + decoder.hpp \ + devpoll.hpp \ + dispatcher.hpp \ + dummy_aggregator.hpp \ + dummy_distributor.hpp \ + encoder.hpp \ + epoll.hpp \ + err.hpp \ + fair_aggregator.hpp \ + fd.hpp \ + fd_signaler.hpp \ + io_object.hpp \ + io_thread.hpp \ + ip.hpp \ + i_api.hpp \ + i_demux.hpp \ + i_mux.hpp \ + i_poller.hpp \ + i_poll_events.hpp \ + i_session.hpp \ + i_signaler.hpp \ + i_engine.hpp \ + i_thread.hpp \ + listener.hpp \ + kqueue.hpp \ + load_balancer.hpp \ + msg.hpp \ + mutex.hpp \ + object.hpp \ + p2p.hpp \ + pipe.hpp \ + pipe_reader.hpp \ + pipe_writer.hpp \ + platform.hpp \ + poll.hpp \ + pub.hpp \ + rep.hpp \ + req.hpp \ + safe_object.hpp \ + select.hpp \ + session.hpp \ + session_stub.hpp \ + simple_semaphore.hpp \ + socket_base.hpp \ + sub.hpp \ + stdint.hpp \ + tcp_connecter.hpp \ + tcp_listener.hpp \ + tcp_socket.hpp \ + thread.hpp \ + uuid.hpp \ + windows.hpp \ + wire.hpp \ + ypipe.hpp \ + ypollset.hpp \ + yqueue.hpp \ + zmq_decoder.hpp \ + zmq_encoder.hpp \ + zmq_tcp_engine.hpp \ + app_thread.cpp \ + connecter.cpp \ + data_distributor.cpp \ + devpoll.hpp \ + dispatcher.cpp \ + dummy_aggregator.cpp \ + dummy_distributor.cpp \ + epoll.cpp \ + err.cpp \ + fair_aggregator.cpp \ + fd_signaler.cpp \ + io_object.cpp \ + io_thread.cpp \ + ip.cpp \ + kqueue.cpp \ + listener.cpp \ + load_balancer.cpp \ + object.cpp \ + p2p.cpp \ + pipe.cpp \ + pipe_reader.cpp \ + pipe_writer.cpp \ + poll.cpp \ + pub.cpp \ + rep.cpp \ + req.cpp \ + safe_object.cpp \ + select.cpp \ + session.cpp \ + session_stub.cpp \ + socket_base.cpp \ + sub.cpp \ + tcp_connecter.cpp \ + tcp_listener.cpp \ + tcp_socket.cpp \ + thread.cpp \ + uuid.cpp \ + ypollset.cpp \ + zmq_decoder.cpp \ + zmq_encoder.cpp \ + zmq_tcp_engine.cpp \ + zs.cpp + +libzs_la_LDFLAGS = -version-info 0:0:0 +libzs_la_CXXFLAGS = -Wall -pedantic -Werror @ZS_EXTRA_CXXFLAGS@ + +dist-hook: + -rm $(distdir)/src/platform.hpp + + diff --git a/src/app_thread.cpp b/src/app_thread.cpp new file mode 100644 index 0000000..ca08976 --- /dev/null +++ b/src/app_thread.cpp @@ -0,0 +1,221 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#if defined ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#endif + +#include "app_thread.hpp" +#include "dispatcher.hpp" +#include "err.hpp" +#include "session.hpp" +#include "pipe.hpp" +#include "config.hpp" +#include "i_api.hpp" +#include "dummy_aggregator.hpp" +#include "fair_aggregator.hpp" +#include "dummy_distributor.hpp" +#include "data_distributor.hpp" +#include "load_balancer.hpp" +#include "p2p.hpp" +#include "pub.hpp" +#include "sub.hpp" +#include "req.hpp" +#include "rep.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 ZS_DELAY_COMMANDS +#endif + +zs::app_thread_t::app_thread_t (dispatcher_t *dispatcher_, int thread_slot_) : + object_t (dispatcher_, thread_slot_), + tid (0), + last_processing_time (0) +{ +} + +void zs::app_thread_t::shutdown () +{ + // Deallocate all the sessions associated with the thread. + while (!sessions.empty ()) + sessions [0]->shutdown (); + + delete this; +} + +zs::app_thread_t::~app_thread_t () +{ +} + +void zs::app_thread_t::attach_session (session_t *session_) +{ + session_->set_index (sessions.size ()); + sessions.push_back (session_); +} + +void zs::app_thread_t::detach_session (session_t *session_) +{ + // O(1) removal of the session from the list. + sessions_t::size_type i = session_->get_index (); + sessions [i] = sessions [sessions.size () - 1]; + sessions [i]->set_index (i); + sessions.pop_back (); +} + +zs::i_poller *zs::app_thread_t::get_poller () +{ + zs_assert (false); +} + +zs::i_signaler *zs::app_thread_t::get_signaler () +{ + return &pollset; +} + +bool zs::app_thread_t::is_current () +{ + return !sessions.empty () && tid == getpid (); +} + +bool zs::app_thread_t::make_current () +{ + // If there are object managed by this slot we cannot assign the slot + // to a different thread. + if (!sessions.empty ()) + return false; + + tid = getpid (); + return true; +} + +zs::i_api *zs::app_thread_t::create_socket (int type_) +{ + i_mux *mux = NULL; + i_demux *demux = NULL; + session_t *session = NULL; + i_api *api = NULL; + + switch (type_) { + case ZS_P2P: + mux = new dummy_aggregator_t; + zs_assert (mux); + demux = new dummy_distributor_t; + zs_assert (demux); + session = new session_t (this, this, mux, demux, true, false); + zs_assert (session); + api = new p2p_t (this, session); + zs_assert (api); + break; + case ZS_PUB: + demux = new data_distributor_t; + zs_assert (demux); + session = new session_t (this, this, mux, demux, true, false); + zs_assert (session); + api = new pub_t (this, session); + zs_assert (api); + break; + case ZS_SUB: + mux = new fair_aggregator_t; + zs_assert (mux); + session = new session_t (this, this, mux, demux, true, false); + zs_assert (session); + api = new sub_t (this, session); + zs_assert (api); + break; + case ZS_REQ: + // TODO + zs_assert (false); + api = new req_t (this, session); + zs_assert (api); + break; + case ZS_REP: + // TODO + zs_assert (false); + api = new rep_t (this, session); + zs_assert (api); + break; + default: + errno = EINVAL; + return NULL; + } + + attach_session (session); + + return api; +} + +void zs::app_thread_t::process_commands (bool block_) +{ + ypollset_t::signals_t signals; + if (block_) + signals = pollset.poll (); + else { + +#if defined ZS_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). + + // 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; + last_processing_time = current_time; +#endif + + // Check whether there are any commands pending for this thread. + signals = pollset.check (); + } + + if (signals) { + + // Traverse all the possible sources of commands and process + // all the commands from all of them. + for (int i = 0; i != thread_slot_count (); i++) { + if (signals & (ypollset_t::signals_t (1) << i)) { + command_t cmd; + while (dispatcher->read (i, get_thread_slot (), &cmd)) + cmd.destination->process_command (cmd); + } + } + } +} diff --git a/src/app_thread.hpp b/src/app_thread.hpp new file mode 100644 index 0000000..61e7ff1 --- /dev/null +++ b/src/app_thread.hpp @@ -0,0 +1,95 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_APP_THREAD_HPP_INCLUDED__ +#define __ZS_APP_THREAD_HPP_INCLUDED__ + +#include + +#include "i_thread.hpp" +#include "stdint.hpp" +#include "object.hpp" +#include "ypollset.hpp" + +namespace zs +{ + + class app_thread_t : public object_t, public i_thread + { + public: + + app_thread_t (class dispatcher_t *dispatcher_, int thread_slot_); + + // To be called when the whole infrastrucure is being closed (zs_term). + void shutdown (); + + // Returns signaler associated with this application thread. + i_signaler *get_signaler (); + + // Create socket engine in this thread. Return false if the calling + // thread doesn't match the thread handled by this app thread object. + struct i_api *create_socket (int type_); + + // Nota bene: The following two functions are accessed from different + // threads. The caller (dispatcher) is responsible for synchronisation + // of accesses. + + // Returns true is current thread is associated with the app thread. + bool is_current (); + + // Tries to associate current thread with the app thread object. + // Returns true is successfull, false otherwise. + bool make_current (); + + // Processes commands sent to this thread (if any). If 'block' is + // set to true, returns only after at least one command was processed. + void process_commands (bool block_); + + // i_thread implementation. + void attach_session (class session_t *session_); + void detach_session (class session_t *session_); + struct i_poller *get_poller (); + + private: + + // Clean-up. + ~app_thread_t (); + + // Thread ID associated with this slot. + // TODO: Virtualise pid_t! + // TODO: Check whether getpid returns unique ID for each thread. + int tid; + + // Vector of all sessionss associated with this app thread. + typedef std::vector sessions_t; + sessions_t sessions; + + // App thread's signaler object. + ypollset_t pollset; + + // Timestamp of when commands were processed the last time. + uint64_t last_processing_time; + + app_thread_t (const app_thread_t&); + void operator = (const app_thread_t&); + }; + +} + +#endif diff --git a/src/atomic.hpp b/src/atomic.hpp new file mode 100644 index 0000000..e24b719 --- /dev/null +++ b/src/atomic.hpp @@ -0,0 +1,310 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ATOMIC_HPP_INCLUDED__ +#define __ZS_ATOMIC_HPP_INCLUDED__ + +#include "stdint.hpp" + +#if defined ZS_FORCE_MUTEXES +#define ZS_ATOMIC_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZS_ATOMIC_X86 +#elif defined ZMQ_HAVE_WINDOWS +#define ZS_ATOMIC_WINDOWS +#elif defined ZMQ_HAVE_SOLARIS +#define ZS_ATOMIC_SOLARIS +#else +#define ZS_ATOMIC_MUTEX +#endif + +namespace zs +{ + + // Atomic assignement. + inline void atomic_uint32_set (volatile uint32_t *p_, uint32_t value_) + { + *p_ = value_; + // StoreLoad memory barrier should go here on platforms with + // memory models that require it. + } + + // Atomic retrieval of an integer. + inline uint32_t atomic_uint32_get (volatile uint32_t *p_) + { + // StoreLoad memory barrier should go here on platforms with + // memory models that require it. + return *p_; + } + + // Atomic addition. Returns the old value. + inline uint32_t atomic_uint32_add (volatile uint32_t *p_, uint32_t delta_) + { +#if defined ZS_ATOMIC_WINDOWS + return InterlockedExchangeAdd ((LONG*) &value, increment_); +#elif defined ZS_ATOMIC_SOLARIS + return atomic_add_32_nv (&value, increment_) - delta_; +#elif defined ZS_ATOMIC_X86 + uint32_t old; + __asm__ volatile ( + "lock; xadd %0, %1\n\t" + : "=r" (old), "=m" (*p_) + : "0" (delta_), "m" (*p_) + : "cc", "memory"); + return old; +#else +#error // TODO: + sync.lock (); + uint32_t old = *p_; + *p_ += delta_; + sync.unlock (); +#endif + } + + // Atomic subtraction. Returns the old value. + inline uint32_t atomic_uint32_sub (volatile uint32_t *p_, uint32_t delta_) + { +#if defined ZS_ATOMIC_WINDOWS + LONG delta = - ((LONG) delta_); + return InterlockedExchangeAdd ((LONG*) &value, delta); +#elif defined ZS_ATOMIC_SOLARIS + int32_t delta = - ((int32_t) delta_); + return atomic_add_32_nv (&value, delta) + delta_; +#elif defined ZS_ATOMIC_X86 + uint32_t old = -delta_; + __asm__ volatile ("lock; xaddl %0,%1" + : "=r" (old), "=m" (*p_) + : "0" (old), "m" (*p_) + : "cc"); + return old; +#else +#error // TODO: + sync.lock (); + uint32_t old = *p_; + *p_ -= delta_; + sync.unlock (); + return old; +#endif + } + + // Atomic assignement. + template + inline void atomic_ptr_set (volatile T **p_, T *value_) + { + *p_ = value_; + // StoreLoad memory barrier should go here on platforms with + // memory models that require it. + } + + // Perform atomic 'exchange pointers' operation. Old value is returned. + template + inline void *atomic_ptr_xchg (volatile T **p_, T *value_) + { +#if defined ZS_ATOMIC_WINDOWS + return InterlockedExchangePointer (p_, value_); +#elif defined ZS_ATOMIC_SOLARIS + return atomic_swap_ptr (p_, value_); +#elif defined ZS_ATOMIC_X86 + void *old; + __asm__ volatile ( + "lock; xchg %0, %2" + : "=r" (old), "=m" (*p_) + : "m" (*p_), "0" (value_)); + return old; +#else +#error // TODO: + sync.lock (); + void *old = *p_; + *p_ = value_; + sync.unlock (); + return old; +#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 'value'. Old value of the pointer + // is returned. + template + inline void *atomic_ptr_cas (volatile T **p_, T *cmp_, T *value_) + { +#if defined ZS_ATOMIC_WINDOWS + return InterlockedCompareExchangePointer (p_, value_, cmp_); +#elif defined ZS_ATOMIC_SOLARIS + return atomic_cas_ptr (p_, cmp_, value_); +#elif defined ZS_ATOMIC_X86 + void *old; + __asm__ volatile ( + "lock; cmpxchg %2, %3" + : "=a" (old), "=m" (*p_) + : "r" (value_), "m" (*p_), "0" (cmp_) + : "cc"); + return old; +#else +#error // TODO: + sync.lock (); + void *old = *p_; + if (old == cmp_) + *p_ = value_; + sync.unlock (); + return old; +#endif + } + +#if defined ZS_ATOMIC_X86 && defined __x86_64__ + typedef uint64_t atomic_bitmap_t; +#else + typedef uint32_t atomic_bitmap_t; +#endif + + // Atomic assignement. + inline void atomic_bitmap_set (volatile atomic_bitmap_t *p_, + atomic_bitmap_t value_) + { + *p_ = value_; + // StoreLoad memory barrier should go here on platforms with + // memory models that require it. + } + + // Bit-test-set-and-reset. Sets one bit of the value and resets + // another one. Returns the original value of the reset bit. + inline bool atomic_bitmap_btsr (volatile atomic_bitmap_t *p_, + int set_index_, int reset_index_) + { +#if defined ZS_ATOMIC_WINDOWS + while (true) { + atomic_bitmap_t oldval = *p_; + atomic_bitmap_t newval = (oldval | (atomic_bitmap_t (1) << + set_index_)) & ~(integer_t (1) << reset_index_); + if (InterlockedCompareExchange ((volatile LONG*) p_, newval, + oldval) == (LONG) oldval) + return (oldval & (atomic_bitmap_t (1) << reset_index_)) ? + true : false; + } +#elif defined ZS_ATOMIC_SOLARIS + while (true) { + atomic_bitmap_t oldval = *p_; + atomic_bitmap_t newval = (oldval | (atomic_bitmap_t (1) << + set_index_)) & ~(integer_t (1) << reset_index_); + if (atomic_cas_32 (p_, oldval, newval) == oldval) + return (oldval & (atomic_bitmap_t (1) << reset_index_)) ? + true : false; + } +#elif defined ZS_ATOMIC_X86 + atomic_bitmap_t oldval, dummy; + __asm__ volatile ( + "mov %0, %1\n\t" + "1:" + "mov %1, %2\n\t" + "bts %3, %2\n\t" + "btr %4, %2\n\t" + "lock cmpxchg %2, %0\n\t" + "jnz 1b\n\t" + : "+m" (*p_), "=&a" (oldval), "=&r" (dummy) + : "r" (atomic_bitmap_t (set_index_)), + "r" (atomic_bitmap_t (reset_index_)) + : "cc"); + return (bool) (oldval & (atomic_bitmap_t (1) << reset_index_)); +#else +#error // TODO: + sync.lock (); + atomic_bitmap_t oldval = *p_; + *p_ = (oldval | (atomic_bitmap_t (1) << set_index_)) & + ~(atomic_bitmap_t (1) << reset_index_); + sync.unlock (); + return (oldval & (atomic_bitmap_t (1) << reset_index_)) ? true : false; +#endif + } + + // Sets value to newval. Returns the original value. + inline atomic_bitmap_t atomic_bitmap_xchg (volatile atomic_bitmap_t *p_, + atomic_bitmap_t newval_) + { +#if defined ZS_ATOMIC_WINDOWS + return InterlockedExchange ((volatile LONG*) p_, newval_); +#elif defined ZS_ATOMIC_SOLARIS + return atomic_swap_32 (p_, newval_); +#elif defined ZS_ATOMIC_X86 + atomic_bitmap_t oldval = newval_; + __asm__ volatile ( + "lock; xchg %0, %1" + : "=r" (oldval) + : "m" (*p_), "0" (oldval) + : "memory"); + return oldval; +#else +#error // TODO: + sync.lock (); + atomic_bitmap_t oldval = *p_; + *p_ = newval_; + sync.unlock (); +#endif + } + + // izte is "if-zero-then-else" atomic operation - if the value is zero + // it substitutes it by 'thenval' else it rewrites it by 'elseval'. + // Original value of the integer is returned from this function. + inline atomic_bitmap_t atomic_bitmap_izte (volatile atomic_bitmap_t *p_, + atomic_bitmap_t thenval_, atomic_bitmap_t elseval_) + { +#if defined ZS_ATOMIC_WINDOWS + while (true) { + atomic_bitmap_t oldval = *p_; + atomic_bitmap_t newval = (oldval ? elseval_ : thenval_); + if (InterlockedCompareExchange ((volatile LONG*) p_, newval, + oldval) == (LONG) oldval) + return oldval; + } +#elif defined ZS_ATOMIC_SOLARIS + while (true) { + atomic_bitmap_t oldval = *p_; + atomic_bitmap_t newval = (oldval ? elseval_ : thenval_); + if (atomic_cas_32 (p_, oldval, newval) == oldval) + return oldval; + } +#elif defined ZS_ATOMIC_X86 + atomic_bitmap_t oldval; + atomic_bitmap_t dummy; + __asm__ volatile ( + "mov %0, %1\n\t" + "1:" + "mov %3, %2\n\t" + "test %1, %1\n\t" + "jz 2f\n\t" + "mov %4, %2\n\t" + "2:" + "lock cmpxchg %2, %0\n\t" + "jnz 1b\n\t" + : "+m" (*p_), "=&a" (oldval), "=&r" (dummy) + : "r" (thenval_), "r" (elseval_) + : "cc"); + return oldval; +#else +#error // TODO: + sync.lock (); + atomic_bitmap_t oldval = *p_; + *p_ = oldval ? elseval_ : thenval_; + sync.unlock (); + return oldval; +#endif + } + +} + +#endif diff --git a/src/atomic_bitmap.hpp b/src/atomic_bitmap.hpp new file mode 100644 index 0000000..a5440de --- /dev/null +++ b/src/atomic_bitmap.hpp @@ -0,0 +1,286 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ATOMIC_BITMAP_HPP_INCLUDED__ +#define __ZS_ATOMIC_BITMAP_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "platform.hpp" + +// These are the conditions to choose between different implementations +// of atomic_bitmap. + +#if defined ZS_FORCE_MUTEXES +#define ZS_ATOMIC_BITMAP_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZS_ATOMIC_BITMAP_X86 +#elif 0 && defined __sparc__ && defined __GNUC__ +#define ZS_ATOMIC_BITMAP_SPARC +#elif defined ZS_HAVE_WINDOWS +#define ZS_ATOMIC_BITMAP_WINDOWS +#elif defined ZS_HAVE_SOLARIS +#define ZS_ATOMIC_BITMAP_SOLARIS +#else +#define ZS_ATOMIC_BITMAP_MUTEX +#endif + +#if defined ZS_ATOMIC_BITMAP_MUTEX +#include "mutex.hpp" +#elif defined ZS_ATOMIC_BITMAP_WINDOWS +#include "windows.hpp" +#elif defined ZS_ATOMIC_BITMAP_SOLARIS +#include +#endif + +namespace zs +{ + + // This class encapuslates several bitwise atomic operations on unsigned + // integer. Selection of operations is driven specifically by the needs + // of ypollset implementation. + + class atomic_bitmap_t + { + public: + +#if (defined ZMQ_ATOMIC_BITMAP_X86 || defined ZMQ_FORCE_MUTEXES) \ + && defined __x86_64__ + typedef uint64_t bitmap_t; +#else + typedef uint32_t bitmap_t; +#endif + + inline atomic_bitmap_t (bitmap_t value_ = 0) : + value (value_) + { + } + + inline ~atomic_bitmap_t () + { + } + + // Bit-test-set-and-reset. Sets one bit of the value and resets + // another one. Returns the original value of the reset bit. + inline bool btsr (int set_index_, int reset_index_) + { +#if defined ZS_ATOMIC_BITMAP_WINDOWS + while (true) { + bitmap_t oldval = value; + bitmap_t newval = (oldval | (bitmap_t (1) << set_index_)) & + ~(bitmap_t (1) << reset_index_); + if (InterlockedCompareExchange ((volatile LONG*) &value, newval, + oldval) == (LONG) oldval) + return (oldval & (bitmap_t (1) << reset_index_)) ? + true : false; + } +#elif defined ZS_ATOMIC_BITMAP_SOLARIS + while (true) { + bitmap_t oldval = value; + bitmap_t newval = (oldval | (bitmap_t (1) << set_index_)) & + ~(bitmap_t (1) << reset_index_); + if (atomic_cas_32 (&value, oldval, newval) == oldval) + return (oldval & (bitmap_t (1) << reset_index_)) ? + true : false; + } +#elif defined ZS_ATOMIC_BITMAP_X86 + bitmap_t oldval, dummy; + __asm__ volatile ( + "mov %0, %1\n\t" + "1:" + "mov %1, %2\n\t" + "bts %3, %2\n\t" + "btr %4, %2\n\t" + "lock cmpxchg %2, %0\n\t" + "jnz 1b\n\t" + : "+m" (value), "=&a" (oldval), "=&r" (dummy) + : "r" (bitmap_t(set_index_)), "r" (bitmap_t(reset_index_)) + : "cc"); + return (bool) (oldval & (bitmap_t(1) << reset_index_)); +#elif defined ZS_ATOMIC_BITMAP_SPARC + volatile bitmap_t* valptr = &value; + bitmap_t set_val = bitmap_t(1) << set_index_; + bitmap_t reset_val = ~(bitmap_t(1) << reset_index_); + bitmap_t tmp; + bitmap_t oldval; + __asm__ volatile( + "ld [%5], %2 \n\t" + "1: \n\t" + "or %2, %0, %3 \n\t" + "and %3, %1, %3 \n\t" + "cas [%5], %2, %3 \n\t" + "cmp %2, %3 \n\t" + "bne,a,pn %%icc, 1b \n\t" + "mov %3, %2 \n\t" + : "+r" (set_val), "+r" (reset_val), "=&r" (tmp), + "=&r" (oldval), "+m" (*valptr) + : "r" (valptr) + : "cc"); + return oldval; +#elif defined ZS_ATOMIC_BITMAP_MUTEX + sync.lock (); + bitmap_t oldval = value; + value = (oldval | (bitmap_t (1) << set_index_)) & + ~(bitmap_t (1) << reset_index_); + sync.unlock (); + return (oldval & (bitmap_t (1) << reset_index_)) ? true : false; +#else +#error +#endif + } + + // Sets value to newval. Returns the original value. + inline bitmap_t xchg (bitmap_t newval_) + { + bitmap_t oldval; +#if defined ZS_ATOMIC_BITMAP_WINDOWS + oldval = InterlockedExchange ((volatile LONG*) &value, newval_); +#elif defined ZS_ATOMIC_BITMAP_SOLARIS + oldval = atomic_swap_32 (&value, newval_); +#elif defined ZS_ATOMIC_BITMAP_X86 + oldval = newval_; + __asm__ volatile ( + "lock; xchg %0, %1" + : "=r" (oldval) + : "m" (value), "0" (oldval) + : "memory"); +#elif defined ZS_ATOMIC_BITMAP_SPARC + oldval = value; + volatile bitmap_t* ptrin = &value; + bitmap_t tmp; + bitmap_t prev; + __asm__ __volatile__( + "ld [%4], %1\n\t" + "1:\n\t" + "mov %0, %2\n\t" + "cas [%4], %1, %2\n\t" + "cmp %1, %2\n\t" + "bne,a,pn %%icc, 1b\n\t" + "mov %2, %1\n\t" + : "+r" (newval_), "=&r" (tmp), "=&r" (prev), "+m" (*ptrin) + : "r" (ptrin) + : "cc"); + return prev; +#elif defined ZS_ATOMIC_BITMAP_MUTEX + sync.lock (); + oldval = value; + value = newval_; + sync.unlock (); +#else +#error +#endif + return oldval; + } + + // izte is "if-zero-then-else" atomic operation - if the value is zero + // it substitutes it by 'thenval' else it rewrites it by 'elseval'. + // Original value of the integer is returned from this function. + inline bitmap_t izte (bitmap_t thenval_, + bitmap_t elseval_) + { +#if defined ZS_ATOMIC_BITMAP_WINDOWS + while (true) { + bitmap_t oldval = value; + bitmap_t newval = oldval == 0 ? thenval_ : elseval_; + if (InterlockedCompareExchange ((volatile LONG*) &value, + newval, oldval) == (LONG) oldval) + return oldval; + } +#elif defined ZS_ATOMIC_BITMAP_SOLARIS + while (true) { + bitmap_t oldval = value; + bitmap_t newval = oldval == 0 ? thenval_ : elseval_; + if (atomic_cas_32 (&value, oldval, newval) == oldval) + return oldval; + } +#elif defined ZS_ATOMIC_BITMAP_X86 + bitmap_t oldval; + bitmap_t dummy; + __asm__ volatile ( + "mov %0, %1\n\t" + "1:" + "mov %3, %2\n\t" + "test %1, %1\n\t" + "jz 2f\n\t" + "mov %4, %2\n\t" + "2:" + "lock cmpxchg %2, %0\n\t" + "jnz 1b\n\t" + : "+m" (value), "=&a" (oldval), "=&r" (dummy) + : "r" (thenval_), "r" (elseval_) + : "cc"); + return oldval; +#elif defined ZS_ATOMIC_BITMAP_SPARC + volatile bitmap_t* ptrin = &value; + bitmap_t tmp; + bitmap_t prev; + __asm__ __volatile__( + "ld [%3], %0 \n\t" + "mov 0, %1 \n\t" + "cas [%3], %1, %4 \n\t" + "cmp %0, %1 \n\t" + "be,a,pn %%icc,1f \n\t" + "ld [%3], %0 \n\t" + "cas [%3], %0, %5 \n\t" + "1: \n\t" + : "=&r" (tmp), "=&r" (prev), "+m" (*ptrin) + : "r" (ptrin), "r" (thenval_), "r" (elseval_) + : "cc"); + return prev; +#elif defined ZS_ATOMIC_BITMAP_MUTEX + sync.lock (); + bitmap_t oldval = value; + value = oldval ? elseval_ : thenval_; + sync.unlock (); + return oldval; +#else +#error +#endif + } + + private: + + volatile bitmap_t value; +#if defined ZS_ATOMIC_BITMAP_MUTEX + mutex_t sync; +#endif + + atomic_bitmap_t (const atomic_bitmap_t&); + void operator = (const atomic_bitmap_t&); + }; + +} + +// Remove macros local to this file. +#if defined ZS_ATOMIC_BITMAP_WINDOWS +#undef ZS_ATOMIC_BITMAP_WINDOWS +#endif +#if defined ZS_ATOMIC_BITMAP_SOLARIS +#undef ZS_ATOMIC_BITMAP_SOLARIS +#endif +#if defined ZS_ATOMIC_BITMAP_X86 +#undef ZS_ATOMIC_BITMAP_X86 +#endif +#if defined ZS_ATOMIC_BITMAP_SPARC +#undef ZS_ATOMIC_BITMAP_SPARC +#endif +#if defined ZS_ATOMIC_BITMAP_MUTEX +#undef ZS_ATOMIC_BITMAP_MUTEX +#endif + +#endif diff --git a/src/atomic_counter.hpp b/src/atomic_counter.hpp new file mode 100644 index 0000000..0873fdd --- /dev/null +++ b/src/atomic_counter.hpp @@ -0,0 +1,197 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + + +#ifndef __ZS_ATOMIC_COUNTER_HPP_INCLUDED__ +#define __ZS_ATOMIC_COUNTER_HPP_INCLUDED__ + +#include "stdint.hpp" +#include "platform.hpp" + +#if defined ZS_FORCE_MUTEXES +#define ZS_ATOMIC_COUNTER_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZS_ATOMIC_COUNTER_X86 +#elif 0 && defined __sparc__ && defined __GNUC__ +#define ZS_ATOMIC_COUNTER_SPARC +#elif defined ZS_HAVE_WINDOWS +#define ZS_ATOMIC_COUNTER_WINDOWS +#elif defined ZS_HAVE_SOLARIS +#define ZS_ATOMIC_COUNTER_SOLARIS +#else +#define ZS_ATOMIC_COUNTER_MUTEX +#endif + +#if defined ZS_ATOMIC_COUNTER_MUTEX +#include "mutex.hpp" +#elif defined ZS_ATOMIC_COUNTER_WINDOWS +#include "windows.hpp" +#elif defined ZS_ATOMIC_COUNTER_SOLARIS +#include +#endif + +namespace zs +{ + + // 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 ZS_ATOMIC_COUNTER_WINDOWS + old_value = InterlockedExchangeAdd ((LONG*) &value, increment_); +#elif defined ZS_ATOMIC_COUNTER_SOLARIS + integer_t new_value = atomic_add_32_nv (&value, increment_); + old_value = new_value - increment_; +#elif defined ZS_ATOMIC_COUNTER_X86 + __asm__ volatile ( + "lock; xadd %0, %1 \n\t" + : "=r" (old_value), "=m" (value) + : "0" (increment_), "m" (value) + : "cc", "memory"); +#elif defined ZS_ATOMIC_COUNTER_SPARC + integer_t tmp; + __asm__ volatile ( + "ld [%4], %0 \n\t" + "1: \n\t" + "add %0, %3, %1 \n\t" + "cas [%4], %0, %1 \n\t" + "cmp %0, %1 \n\t" + "bne,a,pn %%icc, 1b \n\t" + "mov %1, %0 \n\t" + : "=&r" (old_value), "=&r" (tmp), "=m" (value) + : "r" (increment_), "r" (&value) + : "cc", "memory"); +#elif defined ZS_ATOMIC_COUNTER_MUTEX + sync.lock (); + old_value = value; + value += increment_; + sync.unlock (); +#else +#error +#endif + return old_value; + } + + // Atomic subtraction. Returns false if the counter drops to zero. + inline bool sub (integer_t decrement) + { +#if defined ZS_ATOMIC_COUNTER_WINDOWS + LONG delta = - ((LONG) decrement); + integer_t old = InterlockedExchangeAdd ((LONG*) &value, delta); + return old - decrement != 0; +#elif defined ZS_ATOMIC_COUNTER_SOLARIS + int32_t delta = - ((int32_t) decrement); + integer_t nv = atomic_add_32_nv (&value, delta); + return nv != 0; +#elif defined ZS_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"); + return oldval != decrement; +#elif defined ZS_ATOMIC_COUNTER_SPARC + volatile integer_t *val = &value; + integer_t tmp; + integer_t result; + __asm__ volatile( + "ld [%4], %1\n\t" + "1:\n\t" + "add %1, %0, %2\n\t" + "cas [%4], %1, %2\n\t" + "cmp %1, %2\n\t" + "bne,a,pn %%icc, 1b\n\t" + "mov %2, %1\n\t" + : "+r" (-decrement), "=&r" (tmp), "=&r" (result), "+m" (*val) + : "r" (val) + : "cc"); + return result <= decrement; +#elif defined ZS_ATOMIC_COUNTER_MUTEX + sync.lock (); + value -= decrement; + bool result = value ? true : false; + sync.unlock (); + return result; +#else +#error +#endif + } + + inline integer_t get () + { + return value; + } + + private: + + volatile integer_t value; +#if defined ZS_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 ZS_ATOMIC_COUNTER_WINDOWS +#undef ZS_ATOMIC_COUNTER_WINDOWS +#endif +#if defined ZS_ATOMIC_COUNTER_SOLARIS +#undef ZS_ATOMIC_COUNTER_SOLARIS +#endif +#if defined ZS_ATOMIC_COUNTER_X86 +#undef ZS_ATOMIC_COUNTER_X86 +#endif +#if defined ZS_ATOMIC_COUNTER_SPARC +#undef ZS_ATOMIC_COUNTER_SPARC +#endif +#if defined ZS_ATOMIC_COUNTER_MUTEX +#undef ZS_ATOMIC_COUNTER_MUTEX +#endif + +#endif diff --git a/src/atomic_ptr.hpp b/src/atomic_ptr.hpp new file mode 100644 index 0000000..fcc4e73 --- /dev/null +++ b/src/atomic_ptr.hpp @@ -0,0 +1,189 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + + +#ifndef __ZS_ATOMIC_PTR_HPP_INCLUDED__ +#define __ZS_ATOMIC_PTR_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZS_FORCE_MUTEXES +#define ZS_ATOMIC_PTR_MUTEX +#elif (defined __i386__ || defined __x86_64__) && defined __GNUC__ +#define ZS_ATOMIC_PTR_X86 +#elif 0 && defined __sparc__ && defined __GNUC__ +#define ZS_ATOMIC_PTR_SPARC +#elif defined ZS_HAVE_WINDOWS +#define ZS_ATOMIC_PTR_WINDOWS +#elif defined ZS_HAVE_SOLARIS +#define ZS_ATOMIC_PTR_SOLARIS +#else +#define ZS_ATOMIC_PTR_MUTEX +#endif + +#if defined ZS_ATOMIC_PTR_MUTEX +#include "mutex.hpp" +#elif defined ZS_ATOMIC_PTR_WINDOWS +#include "windows.hpp" +#elif defined ZS_ATOMIC_PTR_SOLARIS +#include +#endif + +namespace zs +{ + + // This class encapsulates several atomic operations on pointers. + + template 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 ZS_ATOMIC_PTR_WINDOWS + return (T*) InterlockedExchangePointer (&ptr, val_); +#elif defined ZS_ATOMIC_PTR_SOLARIS + return (T*) atomic_swap_ptr (&ptr, val_); +#elif defined ZS_ATOMIC_PTR_X86 + T *old; + __asm__ volatile ( + "lock; xchg %0, %2" + : "=r" (old), "=m" (ptr) + : "m" (ptr), "0" (val_)); + return old; +#elif defined ZS_ATOMIC_PTR_SPARC + T* newptr = val_; + volatile T** ptrin = &ptr; + T* tmp; + T* prev; + __asm__ __volatile__( + "ld [%4], %1\n\t" + "1:\n\t" + "mov %0, %2\n\t" + "cas [%4], %1, %2\n\t" + "cmp %1, %2\n\t" + "bne,a,pn %%icc, 1b\n\t" + "mov %2, %1\n\t" + : "+r" (newptr), "=&r" (tmp), "=&r" (prev), "+m" (*ptrin) + : "r" (ptrin) + : "cc"); + return prev; +#elif defined ZS_ATOMIC_PTR_MUTEX + sync.lock (); + T *old = (T*) ptr; + ptr = val_; + sync.unlock (); + return old; +#else +#error +#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 ZS_ATOMIC_PTR_WINDOWS + return (T*) InterlockedCompareExchangePointer ( + (volatile PVOID*) &ptr, val_, cmp_); +#elif defined ZS_ATOMIC_PTR_SOLARIS + return (T*) atomic_cas_ptr (&ptr, cmp_, val_); +#elif defined ZS_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 ZS_ATOMIC_PTR_SPARC + volatile T** ptrin = &ptr; + volatile T* prev = ptr; + __asm__ __volatile__( + "cas [%3], %1, %2\n\t" + : "+m" (*ptrin) + : "r" (cmp_), "r" (val_), "r" (ptrin) + : "cc"); + return prev; +#elif defined ZS_ATOMIC_PTR_MUTEX + sync.lock (); + T *old = (T*) ptr; + if (ptr == cmp_) + ptr = val_; + sync.unlock (); + return old; +#else +#error +#endif + } + + private: + + volatile T *ptr; +#if defined ZS_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 ZS_ATOMIC_PTR_WINDOWS +#undef ZS_ATOMIC_PTR_WINDOWS +#endif +#if defined ZS_ATOMIC_PTR_SOLARIS +#undef ZS_ATOMIC_PTR_SOLARIS +#endif +#if defined ZS_ATOMIC_PTR_X86 +#undef ZS_ATOMIC_PTR_X86 +#endif +#if defined ZS_ATOMIC_PTR_SPARC +#undef ZS_ATOMIC_PTR_SPARC +#endif +#if defined ZS_ATOMIC_PTR_MUTEX +#undef ZS_ATOMIC_PTR_MUTEX +#endif + +#endif diff --git a/src/command.hpp b/src/command.hpp new file mode 100644 index 0000000..0553137 --- /dev/null +++ b/src/command.hpp @@ -0,0 +1,98 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_COMMAND_HPP_INCLUDED__ +#define __ZS_COMMAND_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zs +{ + + // 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, + bind, + head, + tail, + reg, + reg_and_bind, + unreg, + engine, + terminate, + terminate_ack + } type; + + union { + + struct { + } stop; + + struct { + class pipe_reader_t *reader; + class session_t *peer; + } bind; + + struct { + uint64_t bytes; + } tail; + + struct { + uint64_t bytes; + } head; + + struct { + class simple_semaphore_t *smph; + } reg; + + struct { + class session_t *peer; + bool flow_in; + bool flow_out; + } reg_and_bind; + + struct { + class simple_semaphore_t *smph; + } unreg; + + // TODO: Engine object won't be deallocated on terminal shutdown + // while the command is still on the fly! + struct { + class i_engine *engine; + } engine; + + struct { + } terminate; + + struct { + } terminate_ack; + + } args; + }; + +} + +#endif diff --git a/src/config.hpp b/src/config.hpp new file mode 100644 index 0000000..a0569ea --- /dev/null +++ b/src/config.hpp @@ -0,0 +1,71 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_CONFIG_HPP_INCLUDED__ +#define __ZS_CONFIG_HPP_INCLUDED__ + +namespace zs +{ + + // Compile-time settings. + + enum + { + + // 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 new commands in command pipe needed to trigger new memory + // allocation. The number should be kept low to decrease the memory + // footprint of dispatcher. + command_pipe_granularity = 4, + + // 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, + + // 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 + + }; + +} + +#endif diff --git a/src/connecter.cpp b/src/connecter.cpp new file mode 100644 index 0000000..a21dde3 --- /dev/null +++ b/src/connecter.cpp @@ -0,0 +1,189 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "connecter.hpp" +#include "io_thread.hpp" +#include "session.hpp" +#include "err.hpp" +#include "simple_semaphore.hpp" +#include "zmq_tcp_engine.hpp" + +zs::connecter_t::connecter_t (io_thread_t *thread_, const char *addr_, + session_t *session_) : + io_object_t (thread_), + state (idle), + poller (NULL), + session (session_), + addr (addr_), + identity ("abcde"), + engine (NULL) +{ +} + +void zs::connecter_t::terminate () +{ + delete this; +} + +void zs::connecter_t::shutdown () +{ + delete this; +} + +zs::connecter_t::~connecter_t () +{ +} + +void zs::connecter_t::process_reg (simple_semaphore_t *smph_) +{ + // Fet poller pointer for further use. + zs_assert (!poller); + poller = get_poller (); + + // Ask the session to register itself with the I/O thread. Note that + // the session is living in the same I/O thread, thus this results + // in a synchronous call. + session->inc_seqnum (); + send_reg (session, NULL); + + // Unlock the application thread that created the connecter. + if (smph_) + smph_->post (); + + // Manually trigger timer event which will launch asynchronous connect. + state = waiting; + timer_event (); +} + +void zs::connecter_t::process_unreg (simple_semaphore_t *smph_) +{ + // Unregister connecter/engine from the poller. + zs_assert (poller); + if (state == connecting) + poller->rm_fd (handle); + else if (state == waiting) + poller->cancel_timer (this); + else if (state == sending) + engine->terminate (); + + // Unlock the application thread closing the connecter. + if (smph_) + smph_->post (); +} + +void zs::connecter_t::in_event () +{ + // Error occured in asynchronous connect. Retry to connect after a while. + if (state == connecting) { + fd_t fd = tcp_connecter.connect (); + zs_assert (fd == retired_fd); + poller->rm_fd (handle); + poller->add_timer (this); + state = waiting; + return; + } + + zs_assert (false); +} + +void zs::connecter_t::out_event () +{ + if (state == connecting) { + + fd_t fd = tcp_connecter.connect (); + if (fd == retired_fd) { + poller->rm_fd (handle); + poller->add_timer (this); + state = waiting; + return; + } + + poller->rm_fd (handle); + engine = new zmq_tcp_engine_t (fd); + zs_assert (engine); + engine->attach (poller, this); + state = sending; + return; + } + + zs_assert (false); +} + +void zs::connecter_t::timer_event () +{ + zs_assert (state == waiting); + + // Initiate async connect and start polling for its completion. If async + // connect fails instantly, try to reconnect after a while. + int rc = tcp_connecter.open (addr.c_str ()); + if (rc == 0) { + state = connecting; + in_event (); + } + else if (rc == 1) { + handle = poller->add_fd (tcp_connecter.get_fd (), this); + poller->set_pollout (handle); + state = connecting; + } + else { + poller->add_timer (this); + state = waiting; + } +} + +void zs::connecter_t::set_engine (struct i_engine *engine_) +{ + engine = engine_; +} + +bool zs::connecter_t::read (zs_msg *msg_) +{ + zs_assert (state == sending); + + // Deallocate old content of the message just in case. + zs_msg_close (msg_); + + // Send the identity. + zs_msg_init_size (msg_, identity.size ()); + memcpy (zs_msg_data (msg_), identity.c_str (), identity.size ()); + + // Ask engine to unregister from the poller. + i_engine *e = engine; + engine->detach (); + + // Attach the engine to the session. (Note that this is actually + // a synchronous call. + session->inc_seqnum (); + send_engine (session, e); + + state = idle; + + return true; +} + +bool zs::connecter_t::write (struct zs_msg *msg_) +{ + // No incoming messages are accepted till identity is sent. + return false; +} + +void zs::connecter_t::flush () +{ + // No incoming messages are accepted till identity is sent. +} diff --git a/src/connecter.hpp b/src/connecter.hpp new file mode 100644 index 0000000..91dbf17 --- /dev/null +++ b/src/connecter.hpp @@ -0,0 +1,99 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_CONNECTER_HPP_INCLUDED__ +#define __ZS_CONNECTER_HPP_INCLUDED__ + +#include + +#include "../include/zs.h" + +#include "i_poller.hpp" +#include "io_object.hpp" +#include "i_poll_events.hpp" +#include "i_session.hpp" +#include "tcp_connecter.hpp" + +namespace zs +{ + + class connecter_t : public io_object_t, public i_poll_events, + public i_session + { + public: + + connecter_t (class io_thread_t *thread_, const char *addr_, + class session_t *session_); + + void terminate (); + void shutdown (); + + void process_reg (class simple_semaphore_t *smph_); + void process_unreg (class simple_semaphore_t *smph_); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (); + + // i_session implementation + void set_engine (struct i_engine *engine_); + // void shutdown (); + bool read (struct zs_msg *msg_); + bool write (struct zs_msg *msg_); + void flush (); + + private: + + // Clean-up. + ~connecter_t (); + + enum { + idle, + waiting, + connecting, + sending + } state; + + // Cached pointer to the poller. + struct i_poller *poller; + + // Handle of the connecting socket. + handle_t handle; + + // Associated session. It lives in the same I/O thread. + class session_t *session; + + // Address to connect to. + std::string addr; + + // Identity of the connection. + std::string identity; + + tcp_connecter_t tcp_connecter; + + struct i_engine *engine; + + connecter_t (const connecter_t&); + void operator = (const connecter_t&); + }; + +} + +#endif diff --git a/src/data_distributor.cpp b/src/data_distributor.cpp new file mode 100644 index 0000000..8f89c46 --- /dev/null +++ b/src/data_distributor.cpp @@ -0,0 +1,155 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "data_distributor.hpp" +#include "pipe_writer.hpp" +#include "err.hpp" +#include "session.hpp" +#include "msg.hpp" + +zs::data_distributor_t::data_distributor_t () : + session (NULL) +{ +} + +void zs::data_distributor_t::set_session (session_t *session_) +{ + zs_assert (!session); + session = session_; +} + +void zs::data_distributor_t::shutdown () +{ + // No need to deallocate pipes here. They'll be deallocated during the + // shutdown of the dispatcher. + delete this; +} + +void zs::data_distributor_t::terminate () +{ + // Pipe unregisters itself during the call to terminate, so the pipes + // list shinks by one in each iteration. + while (!pipes.empty ()) + pipes [0]->terminate (); + + delete this; +} + +zs::data_distributor_t::~data_distributor_t () +{ +} + +void zs::data_distributor_t::attach_pipe (pipe_writer_t *pipe_) +{ + // Associate demux with a new pipe. + pipe_->set_demux (this); + pipe_->set_index (pipes.size ()); + pipes.push_back (pipe_); +} + +void zs::data_distributor_t::detach_pipe (pipe_writer_t *pipe_) +{ + // Release the reference to the pipe. + int index = pipe_->get_index (); + pipe_->set_index (-1); + pipes [index] = pipes.back (); + pipes [index]->set_index (index); + pipes.pop_back (); +} + +bool zs::data_distributor_t::empty () +{ + return pipes.empty (); +} + +bool zs::data_distributor_t::send (zs_msg *msg_) +{ + int pipes_count = pipes.size (); + + // If there are no pipes available, simply drop the message. + if (pipes_count == 0) { + zs_msg_close (msg_); + zs_msg_init (msg_); + return true; + } + + // TODO: ??? + // First check whether all pipes are available for writing. +// for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it ++) +// if (!(*it)->check_write (msg_)) +// return false; + + // For VSMs the copying is straighforward. + if (msg_->content == (zs_msg_content*) ZS_VSM) { + for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it ++) + write_to_pipe (*it, msg_); + zs_msg_init (msg_); + return true; + } + + // Optimisation for the case when there's only a single pipe + // to send the message to - no refcount adjustment (i.e. atomic + // operations) needed. + if (pipes_count == 1) { + write_to_pipe (*pipes.begin (), msg_); + zs_msg_init (msg_); + return true; + } + + // 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 the -1). + if (msg_->shared) + msg_->content->refcnt.add (pipes_count - 1); + else { + msg_->shared = true; + // TODO: Add memory barrier here. + msg_->content->refcnt.set (pipes_count); + } + + // Push the message to all destinations. + for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it ++) + write_to_pipe (*it, msg_); + + // Detach the original message from the data buffer. + zs_msg_init (msg_); + + return true; +} + +void zs::data_distributor_t::flush () +{ + // Flush all pipes. If there's large number of pipes, it can be pretty + // inefficient (especially if there's new message only in a single pipe). + // Can it be improved? + for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it ++) + (*it)->flush (); +} + +void zs::data_distributor_t::write_to_pipe (class pipe_writer_t *pipe_, + struct zs_msg *msg_) +{ + if (!pipe_->write (msg_)) { + // TODO: Push gap notification to the pipe. + zs_assert (false); + } +} + diff --git a/src/data_distributor.hpp b/src/data_distributor.hpp new file mode 100644 index 0000000..239de31 --- /dev/null +++ b/src/data_distributor.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DATA_DISTRIBUTOR_HPP_INCLUDED__ +#define __ZS_DATA_DISTRIBUTOR_HPP_INCLUDED__ + +#include + +#include + +namespace zs +{ + + // Object to distribute messages to outbound pipes. + + class data_distributor_t : public i_demux + { + public: + + data_distributor_t (); + + // i_demux implementation. + void set_session (class session_t *session_); + void shutdown (); + void terminate (); + void attach_pipe (class pipe_writer_t *pipe_); + void detach_pipe (class pipe_writer_t *pipe_); + bool empty (); + bool send (struct zs_msg *msg_); + void flush (); + + private: + + // Clean-up. + ~data_distributor_t (); + + // Reference to the owner session object. + class session_t *session; + + // Writes the message to the pipe if possible. If it isn't, writes + // a gap notification to the pipe. + void write_to_pipe (class pipe_writer_t *pipe_, struct zs_msg *msg_); + + // The list of outbound pipes. + typedef std::vector pipes_t; + pipes_t pipes; + + data_distributor_t (const data_distributor_t&); + void operator = (const data_distributor_t&); + }; + +} + +#endif diff --git a/src/decoder.hpp b/src/decoder.hpp new file mode 100644 index 0000000..c643df8 --- /dev/null +++ b/src/decoder.hpp @@ -0,0 +1,101 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DECODER_HPP_INCLUDED__ +#define __ZS_DECODER_HPP_INCLUDED__ + +#include +#include +#include + +namespace zs +{ + + // 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 class decoder_t + { + public: + + inline decoder_t () : + read_ptr (NULL), + to_read (0), + next (NULL) + { + } + + // Push the binary data to the decoder. Returns number of bytes + // actually parsed. + inline size_t write (unsigned char *data_, size_t size_) + { + size_t pos = 0; + while (true) { + size_t to_copy = std::min (to_read, size_ - pos); + if (read_ptr) { + memcpy (read_ptr, data_ + pos, to_copy); + read_ptr += to_copy; + } + pos += to_copy; + to_read -= to_copy; + while (!to_read) + if (!(static_cast (this)->*next) ()) + return pos; + if (pos == size_) + return pos; + } + } + + 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_ptr_, size_t to_read_, + step_t next_) + { + read_ptr = (unsigned char*) read_ptr_; + to_read = to_read_; + next = next_; + } + + private: + + unsigned char *read_ptr; + size_t to_read; + step_t next; + + 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..6e3a8c1 --- /dev/null +++ b/src/devpoll.cpp @@ -0,0 +1,224 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" + +#if defined ZS_HAVE_SOLARIS || defined ZS_HAVE_HPUX + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "devpoll.hpp" +#include "err.hpp" +#include "config.hpp" + +zs::devpoll_t::devpoll_t () +{ + // 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); +} + +zs::devpoll_t::~devpoll_t () +{ + close (devpoll_fd); +} + +void zs::devpoll_t::devpoll_ctl (fd_t fd_, short events_) +{ + struct pollfd pfd = {fd_, events_, 0}; + ssize_t rc = write (devpoll_fd, &pfd, sizeof pfd); + zs_assert (rc == sizeof pfd); +} + +zs::handle_t zs::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); + + handle_t handle; + handle.fd = fd_; + return handle; +} + +void zs::devpoll_t::rm_fd (handle_t handle_) +{ + assert (fd_table [handle_.fd].valid); + + devpoll_ctl (handle_.fd, POLLREMOVE); + fd_table [handle_.fd].valid = false; + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zs::devpoll_t::set_pollin (handle_t handle_) +{ + fd_t fd = handle_.fd; + devpoll_ctl (fd, POLLREMOVE); + fd_table [fd].events |= POLLIN; + devpoll_ctl (fd, fd_table [fd].events); +} + +void zs::devpoll_t::reset_pollin (handle_t handle_) +{ + fd_t fd = handle_.fd; + devpoll_ctl (fd, POLLREMOVE); + fd_table [fd].events &= ~((short) POLLIN); + devpoll_ctl (fd, fd_table [fd].events); +} + +void zs::devpoll_t::set_pollout (handle_t handle_) +{ + fd_t fd = handle_.fd; + devpoll_ctl (fd, POLLREMOVE); + fd_table [fd].events |= POLLOUT; + devpoll_ctl (fd, fd_table [fd].events); +} + +void zs::devpoll_t::reset_pollout (handle_t handle_) +{ + fd_t fd = handle_.fd; + devpoll_ctl (fd, POLLREMOVE); + fd_table [fd].events &= ~((short) POLLOUT); + devpoll_ctl (fd, fd_table [fd].events); +} + +void zs::devpoll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zs::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 zs::devpoll_t::get_load () +{ + return load.get (); +} + +void zs::devpoll_t::start () +{ + worker.start (worker_routine, this); +} + +void zs::devpoll_t::stop () +{ + stopping = true; +} + +void zs::devpoll_t::join () +{ + worker.stop (); +} + +bool zs::devpoll_t::loop () +{ + // According to the poll(7d) man page, we can retrieve + // no more then (OPEN_MAX - 1) events. + int nfds = std::min (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 zs::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..56b3b25 --- /dev/null +++ b/src/devpoll.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DEVPOLL_HPP_INCLUDED__ +#define __ZS_DEVPOLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZS_HAVE_SOLARIS || ZS_HAVE_HPUX + +#include + +#include "i_poller.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Implements socket polling mechanism using the Solaris-specific + // "/dev/poll" interface. + + class devpoll_t : public i_poller + { + public: + + devpoll_t (); + virtual ~devpoll_t (); + + // i_poller implementation. + handle_t add_fd (fd_t fd_, 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 (i_poll_events *events_); + void cancel_timer (i_poll_events *events_); + int get_load (); + void start (); + void stop (); + void join (); + + 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_table; + + typedef std::vector 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 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/dispatcher.cpp b/src/dispatcher.cpp new file mode 100644 index 0000000..c468857 --- /dev/null +++ b/src/dispatcher.cpp @@ -0,0 +1,266 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "dispatcher.hpp" +#include "app_thread.hpp" +#include "io_thread.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "pipe_reader.hpp" +#include "pipe_writer.hpp" +#include "session.hpp" +#include "i_api.hpp" + +#if defined ZS_HAVE_WINDOWS +#include "windows.h" +#endif + +zs::dispatcher_t::dispatcher_t (int app_threads_, int io_threads_) +{ +#ifdef ZS_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); + zs_assert (rc == 0); + zs_assert (LOBYTE (wsa_data.wVersion) == 2 && + HIBYTE (wsa_data.wVersion) == 2); +#endif + + // Create application thread proxies. + for (int i = 0; i != app_threads_; i++) { + app_thread_t *app_thread = new app_thread_t (this, i); + zs_assert (app_thread); + app_threads.push_back (app_thread); + signalers.push_back (app_thread->get_signaler ()); + } + + // Create I/O thread objects. + for (int i = 0; i != io_threads_; i++) { + io_thread_t *io_thread = new io_thread_t (this, i + app_threads_); + zs_assert (io_thread); + io_threads.push_back (io_thread); + signalers.push_back (io_thread->get_signaler ()); + } + + // Create command pipe matrix. + command_pipes = new command_pipe_t [signalers.size () * signalers.size ()]; + zs_assert (command_pipes); + + // Launch I/O threads. + for (int i = 0; i != io_threads_; i++) + io_threads [i]->start (); +} + +void zs::dispatcher_t::shutdown () +{ + delete this; +} + +zs::dispatcher_t::~dispatcher_t () +{ + // Ask I/O threads to terminate. + 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++) + io_threads [i]->join (); + + // At this point the current thread is the only thread with access to + // our internal data. Deallocation will be done exclusively in this thread. + for (app_threads_t::size_type i = 0; i != app_threads.size (); i++) + app_threads [i]->shutdown (); + for (io_threads_t::size_type i = 0; i != io_threads.size (); i++) + io_threads [i]->shutdown (); + + delete [] command_pipes; + + // Deallocate all the pipes, pipe readers and pipe writers. + for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it++) { + delete it->pipe; + delete it->reader; + delete it->writer; + } + +#ifdef ZS_HAVE_WINDOWS + // On Windows, uninitialise socket layer. + int rc = WSACleanup (); + wsa_assert (rc != SOCKET_ERROR); +#endif +} + +int zs::dispatcher_t::thread_slot_count () +{ + return signalers.size (); +} + +zs::i_api *zs::dispatcher_t::create_socket (int type_) +{ + threads_sync.lock (); + app_thread_t *thread = choose_app_thread (); + if (!thread) { + threads_sync.unlock (); + return NULL; + } + i_api *s = thread->create_socket (type_); + threads_sync.unlock (); + return s; +} + +zs::app_thread_t *zs::dispatcher_t::choose_app_thread () +{ + // Check whether thread ID is already assigned. If so, return it. + for (app_threads_t::size_type i = 0; i != app_threads.size (); i++) + if (app_threads [i]->is_current ()) + return app_threads [i]; + + // Check whether there's an unused thread slot in the dispatcher. + for (app_threads_t::size_type i = 0; i != app_threads.size (); i++) + if (app_threads [i]->make_current ()) + return app_threads [i]; + + // Thread limit was exceeded. + errno = EMFILE; + return NULL; +} + +zs::io_thread_t *zs::dispatcher_t::choose_io_thread (uint64_t taskset_) +{ + zs_assert (io_threads.size () > 0); + + // Find the I/O thread with minimum load. + int min_load = io_threads [0]->get_load (); + io_threads_t::size_type result = 0; + for (io_threads_t::size_type i = 1; i != io_threads.size (); i++) { + if (!taskset_ || (taskset_ & (uint64_t (1) << i))) { + int load = io_threads [i]->get_load (); + if (load < min_load) { + min_load = load; + result = i; + } + } + } + + return io_threads [result]; +} + +void zs::dispatcher_t::create_pipe (object_t *reader_parent_, + object_t *writer_parent_, uint64_t hwm_, uint64_t lwm_, + pipe_reader_t **reader_, pipe_writer_t **writer_) +{ + // Create the pipe, reader & writer triple. + pipe_t *pipe = new pipe_t; + zs_assert (pipe); + pipe_reader_t *reader = new pipe_reader_t (reader_parent_, pipe, + hwm_, lwm_); + zs_assert (reader); + pipe_writer_t *writer = new pipe_writer_t (writer_parent_, pipe, reader, + hwm_, lwm_); + zs_assert (writer); + reader->set_peer (writer); + + // Store the pipe in the repository. + pipe_info_t info = {pipe, reader, writer}; + pipes_sync.lock (); + pipe->set_index (pipes.size ()); + pipes.push_back (info); + pipes_sync.unlock (); + + *reader_ = reader; + *writer_ = writer; +} + +void zs::dispatcher_t::destroy_pipe (pipe_t *pipe_) +{ + // Remove the pipe from the repository. + pipe_info_t info; + pipes_sync.lock (); + pipes_t::size_type i = pipe_->get_index (); + info = pipes [i]; + pipes [i] = pipes.back (); + pipes.pop_back (); + pipes_sync.unlock (); + + // Deallocate the pipe and associated pipe reader & pipe writer. + zs_assert (info.pipe == pipe_); + delete info.pipe; + delete info.reader; + delete info.writer; +} + +int zs::dispatcher_t::register_inproc_endpoint (const char *endpoint_, + session_t *session_) +{ + inproc_endpoint_sync.lock (); + inproc_endpoints_t::iterator it = inproc_endpoints.find (endpoint_); + + if (it != inproc_endpoints.end ()) { + inproc_endpoint_sync.unlock (); + errno = EADDRINUSE; + return -1; + } + + inproc_endpoints.insert (std::make_pair (endpoint_, session_)); + + inproc_endpoint_sync.unlock (); + return 0; +} + +zs::object_t *zs::dispatcher_t::get_inproc_endpoint (const char *endpoint_) +{ + inproc_endpoint_sync.lock (); + inproc_endpoints_t::iterator it = inproc_endpoints.find (endpoint_); + + if (it == inproc_endpoints.end ()) { + inproc_endpoint_sync.unlock (); + errno = EADDRNOTAVAIL; + return NULL; + } + + it->second->inc_seqnum (); + object_t *session = it->second; + + inproc_endpoint_sync.unlock (); + return session; +} + +void zs::dispatcher_t::unregister_inproc_endpoints (session_t *session_) +{ + inproc_endpoint_sync.lock (); + + // Remove the connection from the repository. + // TODO: Yes, the algorithm has O(n^2) complexity. Should be O(log n). + for (inproc_endpoints_t::iterator it = inproc_endpoints.begin (); + it != inproc_endpoints.end ();) { + if (it->second == session_) { + inproc_endpoints.erase (it); + it = inproc_endpoints.begin (); + } + else + it++; + } + + inproc_endpoint_sync.unlock (); +} + diff --git a/src/dispatcher.hpp b/src/dispatcher.hpp new file mode 100644 index 0000000..05d2c49 --- /dev/null +++ b/src/dispatcher.hpp @@ -0,0 +1,170 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DISPATCHER_HPP_INCLUDED__ +#define __ZS_DISPATCHER_HPP_INCLUDED__ + +#include +#include +#include + +#include "i_signaler.hpp" +#include "ypipe.hpp" +#include "command.hpp" +#include "config.hpp" +#include "mutex.hpp" +#include "stdint.hpp" + +namespace zs +{ + + // Dispatcher implements bidirectional thread-safe passing of commands + // between N threads. It consists of a ypipes to pass commands and + // signalers to wake up the receiver thread when new commands are + // available. Note that dispatcher is inefficient for passing messages + // within a thread (sender thread = receiver thread). The optimisation is + // not part of the class and should be implemented by individual threads + // (presumably by calling the command handling function directly). + + class dispatcher_t + { + public: + + // Create the dispatcher object. Matrix of pipes to communicate between + // each socket and each I/O thread is created along with appropriate + // signalers. + dispatcher_t (int app_threads_, int io_threads_); + + // To be called to terminate the whole infrastructure (zs_term). + void shutdown (); + + // Create a socket engine. + struct i_api *create_socket (int type_); + + // Returns number of thread slots in the dispatcher. To be used by + // individual threads to find out how many distinct signals can be + // received. + int thread_slot_count (); + + // Write command to the dispatcher. + inline void write (int source_, int destination_, + const command_t &command_) + { + command_pipe_t &pipe = + command_pipes [source_ * signalers.size () + destination_]; + pipe.write (command_); + if (!pipe.flush ()) + signalers [destination_]->signal (source_); + } + + // Read command from the dispatcher. Returns false if there is no + // command available. + inline bool read (int source_, int destination_, command_t *command_) + { + return command_pipes [source_ * signalers.size () + + destination_].read (command_); + } + + // Creates new pipe. + void create_pipe (class object_t *reader_parent_, + class object_t *writer_parent_, uint64_t hwm_, uint64_t lwm_, + class pipe_reader_t **reader_, class pipe_writer_t **writer_); + + // Deallocates the pipe. + void destroy_pipe (class pipe_t *pipe_); + + // Registers existing session object as an inproc endpoint. + int register_inproc_endpoint (const char *endpoint_, + class session_t *session_); + + // Retrieves an inproc endpoint. Increments the command sequence number + // of the object by one. Caller is thus bound to send the command + // to the connection after invoking this function. Returns NULL if + // the endpoint doesn't exist. + class object_t *get_inproc_endpoint (const char *endpoint_); + + // Removes all the inproc endpoints associated with the given session + // object from the global repository. + void unregister_inproc_endpoints (class session_t *session_); + + // 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_); + + private: + + // Clean-up. + ~dispatcher_t (); + + // Returns the app thread associated with the current thread. + // NULL if we are out of app thread slots. + class app_thread_t *choose_app_thread (); + + // Application threads. + typedef std::vector app_threads_t; + app_threads_t app_threads; + + // I/O threads. + typedef std::vector io_threads_t; + io_threads_t io_threads; + + // Signalers for both application and I/O threads. + std::vector signalers; + + // Pipe to hold the commands. + typedef ypipe_t command_pipe_t; + + // NxN matrix of command pipes. + command_pipe_t *command_pipes; + + // Synchronisation of accesses to shared thread data. + mutex_t threads_sync; + + // Global repository of pipes. It's used only on terminal shutdown + // to deallocate all the pipes irrespective of whether they are + // referenced from pipe_reader, pipe_writer or both. + struct pipe_info_t + { + class pipe_t *pipe; + class pipe_reader_t *reader; + class pipe_writer_t *writer; + }; + typedef std::vector pipes_t; + pipes_t pipes; + + // Synchronisation of access to global repository of pipes. + mutex_t pipes_sync; + + // Global repository of available inproc endpoints. + typedef std::map inproc_endpoints_t; + inproc_endpoints_t inproc_endpoints; + + // Synchronisation of access to the global repository + // of inproc endpoints. + mutex_t inproc_endpoint_sync; + + dispatcher_t (const dispatcher_t&); + void operator = (const dispatcher_t&); + }; + +} + +#endif + diff --git a/src/dummy_aggregator.cpp b/src/dummy_aggregator.cpp new file mode 100644 index 0000000..ef0cea6 --- /dev/null +++ b/src/dummy_aggregator.cpp @@ -0,0 +1,111 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "dummy_aggregator.hpp" +#include "err.hpp" +#include "pipe_reader.hpp" +#include "session.hpp" + +// Swaps pipes at specified indices. +#define swap_pipes(i1_, i2_) \ + std::swap (pipes [i1_], pipes [i2_]);\ + pipes [i1_]->set_index (i1_);\ + pipes [i2_]->set_index (i2_); + +zs::dummy_aggregator_t::dummy_aggregator_t () : + session (NULL), + pipe (NULL), + active (false) +{ +} + +void zs::dummy_aggregator_t::set_session (session_t *session_) +{ + zs_assert (!session); + session = session_; +} + +void zs::dummy_aggregator_t::shutdown () +{ + // No need to deallocate the pipe here. It'll be deallocated during the + // shutdown of the dispatcher. + delete this; +} + +void zs::dummy_aggregator_t::terminate () +{ + if (pipe) + pipe->terminate (); + + delete this; +} + +zs::dummy_aggregator_t::~dummy_aggregator_t () +{ +} + +void zs::dummy_aggregator_t::attach_pipe (pipe_reader_t *pipe_) +{ + zs_assert (!pipe); + pipe = pipe_; + active = true; + + // Associate new pipe with the mux object. + pipe_->set_mux (this); + session->revive (); +} + +void zs::dummy_aggregator_t::detach_pipe (pipe_reader_t *pipe_) +{ + zs_assert (pipe == pipe_); + deactivate (pipe_); + pipe = NULL; +} + +bool zs::dummy_aggregator_t::empty () +{ + return pipe == NULL; +} + +bool zs::dummy_aggregator_t::recv (zs_msg *msg_) +{ + // Deallocate old content of the message. + zs_msg_close (msg_); + + // Try to read from the pipe. + if (pipe && pipe->read (msg_)) + return true; + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + zs_msg_init (msg_); + return false; +} + +void zs::dummy_aggregator_t::deactivate (pipe_reader_t *pipe_) +{ + active = false; +} + +void zs::dummy_aggregator_t::reactivate (pipe_reader_t *pipe_) +{ + active = true; +} diff --git a/src/dummy_aggregator.hpp b/src/dummy_aggregator.hpp new file mode 100644 index 0000000..ab5bcb9 --- /dev/null +++ b/src/dummy_aggregator.hpp @@ -0,0 +1,73 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DUMMY_AGGREGATOR_HPP_INCLUDED__ +#define __ZS_DUMMY_AGGREGATOR_HPP_INCLUDED__ + +#include + +#include "i_mux.hpp" + +namespace zs +{ + + // Fake message aggregator. There can be at most one pipe bound to it, + // so there's no real aggregation going on. However, it is more efficient + // than a real aggregator. It's intended to be used in the contexts + // where business logic ensures there'll be at most one pipe bound. + + class dummy_aggregator_t : public i_mux + { + public: + + dummy_aggregator_t (); + + // i_mux interface implementation. + void set_session (session_t *session_); + void shutdown (); + void terminate (); + void attach_pipe (class pipe_reader_t *pipe_); + void detach_pipe (class pipe_reader_t *pipe_); + bool empty (); + void deactivate (class pipe_reader_t *pipe_); + void reactivate (class pipe_reader_t *pipe_); + bool recv (struct zs_msg *msg_); + + + private: + + // Clean-up. + ~dummy_aggregator_t (); + + // Reference to the owner session object. + class session_t *session; + + // The single pipe bound. + class pipe_reader_t *pipe; + + // If true, the pipe is active. + bool active; + + dummy_aggregator_t (const dummy_aggregator_t&); + void operator = (const dummy_aggregator_t&); + }; + +} + +#endif diff --git a/src/dummy_distributor.cpp b/src/dummy_distributor.cpp new file mode 100644 index 0000000..58cadfe --- /dev/null +++ b/src/dummy_distributor.cpp @@ -0,0 +1,85 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "dummy_distributor.hpp" +#include "pipe_writer.hpp" +#include "err.hpp" +#include "session.hpp" +#include "msg.hpp" + +zs::dummy_distributor_t::dummy_distributor_t () : + session (NULL) +{ +} + +void zs::dummy_distributor_t::set_session (session_t *session_) +{ + zs_assert (!session); + session = session_; +} + +void zs::dummy_distributor_t::shutdown () +{ + // No need to deallocate pipe here. It'll be deallocated during the + // shutdown of the dispatcher. + delete this; +} + +void zs::dummy_distributor_t::terminate () +{ + if (pipe) + pipe->terminate (); + + delete this; +} + +zs::dummy_distributor_t::~dummy_distributor_t () +{ +} + +void zs::dummy_distributor_t::attach_pipe (pipe_writer_t *pipe_) +{ + zs_assert (!pipe); + pipe = pipe_; +} + +void zs::dummy_distributor_t::detach_pipe (pipe_writer_t *pipe_) +{ + zs_assert (pipe == pipe_); + pipe = NULL; +} + +bool zs::dummy_distributor_t::empty () +{ + return pipe == NULL; +} + +bool zs::dummy_distributor_t::send (zs_msg *msg_) +{ + return pipe && pipe->write (msg_); +} + +void zs::dummy_distributor_t::flush () +{ + if (pipe) + pipe->flush (); +} + diff --git a/src/dummy_distributor.hpp b/src/dummy_distributor.hpp new file mode 100644 index 0000000..c200ad6 --- /dev/null +++ b/src/dummy_distributor.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_DUMMY_DISTRIBUTOR_HPP_INCLUDED__ +#define __ZS_DUMMY_DISTRIBUTOR_HPP_INCLUDED__ + +#include + +#include + +namespace zs +{ + + // Fake message distributor. There can be only one pipe bound to it + // so there no real distribution going on. However, it is more efficient + // than a real distributor and should be used where business logic + // ensures there'll be at most one pipe bound. + + class dummy_distributor_t : public i_demux + { + public: + + dummy_distributor_t (); + + // i_demux implementation. + void set_session (class session_t *session_); + void shutdown (); + void terminate (); + void attach_pipe (class pipe_writer_t *pipe_); + void detach_pipe (class pipe_writer_t *pipe_); + bool empty (); + bool send (struct zs_msg *msg_); + void flush (); + + private: + + // Clean-up. + ~dummy_distributor_t (); + + // Reference to the owner session object. + class session_t *session; + + // The bound pipe. + class pipe_writer_t *pipe; + + dummy_distributor_t (const dummy_distributor_t&); + void operator = (const dummy_distributor_t&); + }; + +} + +#endif diff --git a/src/encoder.hpp b/src/encoder.hpp new file mode 100644 index 0000000..1241873 --- /dev/null +++ b/src/encoder.hpp @@ -0,0 +1,108 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ENCODER_HPP_INCLUDED__ +#define __ZS_ENCODER_HPP_INCLUDED__ + +#include +#include +#include + +namespace zs +{ + + // Helper base class for encoders. It implements the state machine that + // fills the outgoing buffer. Derived classes should implement individual + // state machine actions. + + template class encoder_t + { + public: + + inline encoder_t () + { + } + + // The function tries to fill the supplied chunk by binary data. + // Returns the size of data actually filled in. 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 size_t read (unsigned char *data_, size_t size_, + int *offset_ = NULL) + { + int offset = -1; + size_t pos = 0; + + while (pos < size_) { + if (to_write) { + + size_t to_copy = std::min (to_write, size_ - pos); + memcpy (data_ + pos, write_pos, to_copy); + pos += to_copy; + write_pos += to_copy; + to_write -= to_copy; + } + else { + bool more = (static_cast (this)->*next) (); + if (beginning && offset == -1) { + offset = pos; + beginning = false; + } + if (!more) + break; + } + } + + if (offset_) + *offset_ = offset; + + return pos; + } + 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; + + 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..a9780d2 --- /dev/null +++ b/src/epoll.cpp @@ -0,0 +1,214 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" + +#ifdef ZS_HAVE_LINUX + +#include +#include +#include +#include +#include + +#include "epoll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zs::epoll_t::epoll_t () : + stopping (false) +{ + epoll_fd = epoll_create (1); + errno_assert (epoll_fd != -1); +} + +zs::epoll_t::~epoll_t () +{ + close (epoll_fd); + + for (retired_t::iterator it = retired.begin (); it != retired.end (); it ++) + delete *it; +} + +zs::handle_t zs::epoll_t::add_fd (fd_t fd_, i_poll_events *events_) +{ + poll_entry_t *pe = new poll_entry_t; + zs_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); + + handle_t handle; + handle.ptr = pe; + return handle; +} + +void zs::epoll_t::rm_fd (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + 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 zs::epoll_t::set_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->ev.events |= EPOLLIN; + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zs::epoll_t::reset_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->ev.events &= ~((short) EPOLLIN); + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zs::epoll_t::set_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->ev.events |= EPOLLOUT; + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zs::epoll_t::reset_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->ev.events &= ~((short) EPOLLOUT); + int rc = epoll_ctl (epoll_fd, EPOLL_CTL_MOD, pe->fd, &pe->ev); + errno_assert (rc != -1); +} + +void zs::epoll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zs::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 zs::epoll_t::get_load () +{ + return load.get (); +} + +void zs::epoll_t::start () +{ + worker.start (worker_routine, this); +} + +void zs::epoll_t::stop () +{ + stopping = true; +} + +void zs::epoll_t::join () +{ + worker.stop (); +} + +void zs::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 zs::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..a3f5168 --- /dev/null +++ b/src/epoll.hpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_EPOLL_HPP_INCLUDED__ +#define __ZS_EPOLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZS_HAVE_LINUX + +#include +#include + +#include "i_poller.hpp" +//#include "i_poll_events.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // This class implements socket polling mechanism using the Linux-specific + // epoll mechanism. + + class epoll_t : public i_poller + { + public: + + epoll_t (); + virtual ~epoll_t (); + + // i_poller implementation. + handle_t add_fd (fd_t fd_, 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 (i_poll_events *events_); + void cancel_timer (i_poll_events *events_); + int get_load (); + void start (); + void stop (); + void join (); + + 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 retired_t; + retired_t retired; + + // List of all the engines waiting for the timer event. + typedef std::vector 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..92a03ba --- /dev/null +++ b/src/err.cpp @@ -0,0 +1,146 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "err.hpp" +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS + +const char *zs::wsa_error() +{ + int errcode = WSAGetLastError (); + // TODO: This is not a generic way to handle this... + if (errcode == WSAEWOULDBLOCK) + return NULL; + + 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 zs::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 ); + zs_assert (rc); +} + +#endif diff --git a/src/err.hpp b/src/err.hpp new file mode 100644 index 0000000..657eb3d --- /dev/null +++ b/src/err.hpp @@ -0,0 +1,90 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ERR_HPP_INCLUDED__ +#define __ZS_ERR_HPP_INCLUDED__ + +#include +#include +#include +#include + +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#endif + +#ifdef ZS_HAVE_WINDOWS + +namespace zs +{ + + const char *wsa_error (); + void win_error (char *buffer_, size_t buffer_size_); + +} + +// Provides convenient way to check WSA-style errors on Windows. +#define wsa_assert(x) do { if (!(x)){\ + const char *errstr = zs::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 (!(x)) {\ + char errstr [256];\ + zs::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 zs_assert(x) do { if (!(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 (!(x)) {\ + perror (NULL);\ + fprintf (stderr, "%s (%s:%d)\n", #x, __FILE__, __LINE__);\ + abort ();\ +}} while (false) + +// Provides convenient way to check for errors from getaddrinfo. +#define gai_assert(x) do { if (x) {\ + const char *errstr = gai_strerror (x);\ + fprintf (stderr, "%s (%s:%d)\n", errstr, __FILE__, __LINE__);\ + abort ();\ +}} while (false) + +#endif diff --git a/src/fair_aggregator.cpp b/src/fair_aggregator.cpp new file mode 100644 index 0000000..65bfac0 --- /dev/null +++ b/src/fair_aggregator.cpp @@ -0,0 +1,143 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "fair_aggregator.hpp" +#include "err.hpp" +#include "pipe_reader.hpp" +#include "session.hpp" + +// Swaps pipes at specified indices. +#define swap_pipes(i1_, i2_) \ + std::swap (pipes [i1_], pipes [i2_]);\ + pipes [i1_]->set_index (i1_);\ + pipes [i2_]->set_index (i2_); + +zs::fair_aggregator_t::fair_aggregator_t () : + session (NULL), + active (0), + current (0) +{ +} + +void zs::fair_aggregator_t::set_session (session_t *session_) +{ + zs_assert (!session); + session = session_; +} + +void zs::fair_aggregator_t::shutdown () +{ + // No need to deallocate pipes here. They'll be deallocated during the + // shutdown of the dispatcher. + delete this; +} + +void zs::fair_aggregator_t::terminate () +{ + // Pipe unregisters itself during the call to terminate, so the pipes + // list shinks by one in each iteration. + while (!pipes.empty ()) + pipes [0]->terminate (); + + delete this; +} + +zs::fair_aggregator_t::~fair_aggregator_t () +{ +} + +void zs::fair_aggregator_t::attach_pipe (pipe_reader_t *pipe_) +{ + // Associate new pipe with the mux object. + pipe_->set_mux (this); + pipes.push_back (pipe_); + active++; + if (pipes.size () > active) + swap_pipes (pipes.size () - 1, active - 1); + if (active == 1) + session->revive (); +} + +void zs::fair_aggregator_t::detach_pipe (pipe_reader_t *pipe_) +{ + // Move the pipe from the list of active pipes to the list of idle pipes. + deactivate (pipe_); + + // Move the pipe to the end of the idle list and remove it. + swap_pipes (pipe_->get_index (), pipes.size () - 1); + pipes.pop_back (); +} + +bool zs::fair_aggregator_t::empty () +{ + return pipes.empty (); +} + +bool zs::fair_aggregator_t::recv (zs_msg *msg_) +{ + // Deallocate old content of the message. + zs_msg_close (msg_); + + // O(1) fair queueing. Round-robin over the active pipes to get + // next message. + for (pipes_t::size_type i = active; i != 0; i--) { + + // Update current. + current = (current + 1) % active; + + // Try to read from current. + if (pipes [current]->read (msg_)) + return true; + } + + // No message is available. Initialise the output parameter + // to be a 0-byte message. + zs_msg_init (msg_); + return false; +} + +void zs::fair_aggregator_t::deactivate (pipe_reader_t *pipe_) +{ + int index = pipe_->get_index (); + + // Suspend an active pipe. + swap_pipes (index, active - 1); + active--; + + // If the deactiveted pipe is the current one, shift the current one pipe + // backwards so that the pipe that replaced the deactiveted one will be + // processed immediately rather than skipped. + if (index == (int) current) { + index--; + if (index == -1) + index = active - 1; + current = index; + } +} + +void zs::fair_aggregator_t::reactivate (pipe_reader_t *pipe_) +{ + // Revive an idle pipe. + swap_pipes (pipe_->get_index (), active); + active++; + if (active == 1) + session->revive (); +} diff --git a/src/fair_aggregator.hpp b/src/fair_aggregator.hpp new file mode 100644 index 0000000..9e6c3bb --- /dev/null +++ b/src/fair_aggregator.hpp @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_FAIR_AGGREGATOR_HPP_INCLUDED__ +#define __ZS_FAIR_AGGREGATOR_HPP_INCLUDED__ + +#include + +#include "i_mux.hpp" + +namespace zs +{ + + // Object to aggregate messages from inbound pipes. + + class fair_aggregator_t : public i_mux + { + public: + + fair_aggregator_t (); + + // i_mux interface implementation. + void set_session (session_t *session_); + void shutdown (); + void terminate (); + void attach_pipe (class pipe_reader_t *pipe_); + void detach_pipe (class pipe_reader_t *pipe_); + bool empty (); + void deactivate (class pipe_reader_t *pipe_); + void reactivate (class pipe_reader_t *pipe_); + bool recv (struct zs_msg *msg_); + + + private: + + // Clean-up. + ~fair_aggregator_t (); + + // Reference to the owner session object. + class session_t *session; + + // The list of inbound pipes. The active pipes are occupying indices + // from 0 to active-1. Suspended pipes occupy indices from 'active' + // to the end of the array. + typedef std::vector pipes_t; + pipes_t pipes; + + // The number of active pipes. + pipes_t::size_type active; + + // Pipe to retrieve next message from. The messages are retrieved + // from the pipes in round-robin fashion (a.k.a. fair queueing). + pipes_t::size_type current; + + fair_aggregator_t (const fair_aggregator_t&); + void operator = (const fair_aggregator_t&); + }; + +} + +#endif diff --git a/src/fd.hpp b/src/fd.hpp new file mode 100644 index 0000000..4d45ed2 --- /dev/null +++ b/src/fd.hpp @@ -0,0 +1,44 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_FD_HPP_INCLUDED__ +#define __ZS_FD_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS +#include "windows.hpp" +#endif + +namespace zs +{ +#ifdef _MSC_VER +#if (_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/fd_signaler.cpp b/src/fd_signaler.cpp new file mode 100644 index 0000000..005dd86 --- /dev/null +++ b/src/fd_signaler.cpp @@ -0,0 +1,278 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "fd_signaler.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "fd.hpp" + +#if defined ZS_HAVE_OPENVMS +#include +#elif defined ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#include +#endif + +#if defined ZS_HAVE_EVENTFD + +#include + +zs::fd_signaler_t::fd_signaler_t () +{ + // Create eventfd object. + fd = eventfd (0, 0); + errno_assert (fd != -1); + + // Set to non-blocking mode. + int flags = fcntl (fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + int rc = fcntl (fd, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); +} + +zs::fd_signaler_t::~fd_signaler_t () +{ + int rc = close (fd); + errno_assert (rc != -1); +} + +void zs::fd_signaler_t::signal (int signal_) +{ + zs_assert (signal_ >= 0 && signal_ < 64); + signals_t inc = 1; + inc <<= signal_; + ssize_t sz = write (fd, &inc, sizeof (signals_t)); + errno_assert (sz == sizeof (signals_t)); +} + +zs::fd_signaler_t::signals_t zs::fd_signaler_t::check () +{ + signals_t val; + ssize_t sz = read (fd, &val, sizeof (signals_t)); + if (sz == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINTR)) + return 0; + errno_assert (sz != -1); + return val; +} + +zs::fd_t zs::fd_signaler_t::get_fd () +{ + return fd; +} + +#elif defined ZS_HAVE_WINDOWS + +zs::fd_signaler_t::fd_signaler_t () +{ + 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; + resolve_ip_hostname (&addr, "127.0.0.1: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); + + rc = closesocket (listener); + wsa_assert (rc != SOCKET_ERROR); +} + +zs::fd_signaler_t::~fd_signaler_t () +{ + int rc = closesocket (w); + wsa_assert (rc != SOCKET_ERROR); + + rc = closesocket (r); + wsa_assert (rc != SOCKET_ERROR); +} + +void zs::fd_signaler_t::signal (int signal_) +{ + zs_assert (signal_ >= 0 && signal_ < 64); + char c = (char) signal_; + int rc = send (w, &c, 1, 0); + win_assert (rc != SOCKET_ERROR); +} + +zs::fd_signaler_t::signals_t zs::fd_signaler_t::check () +{ + char buffer [32]; + int nbytes = recv (r, buffer, 32, 0); + win_assert (nbytes != -1); + + signals_t signals = 0; + for (int pos = 0; pos != nbytes; pos++) { + zs_assert (buffer [pos] < 64); + signals |= (1 << (buffer [pos])); + } + return signals; +} + +zs::fd_t zs::fd_signaler_t::get_fd () +{ + return r; +} + +#else + +#include +#include + +zs::fd_signaler_t::fd_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 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); +} + +zs::fd_signaler_t::~fd_signaler_t () +{ + close (w); + close (r); +} + +void zs::fd_signaler_t::signal (int signal_) +{ + zs_assert (signal_ >= 0 && signal_ < 64); + unsigned char c = (unsigned char) signal_; + ssize_t nbytes = send (w, &c, 1, 0); + errno_assert (nbytes == 1); +} + +zs::fd_signaler_t::signals_t zs::fd_signaler_t::check () +{ + unsigned char buffer [32]; + ssize_t nbytes = recv (r, buffer, 32, 0); + errno_assert (nbytes != -1); + signals_t signals = 0; + for (int pos = 0; pos != nbytes; pos ++) { + zs_assert (buffer [pos] < 64); + signals |= (1 << (buffer [pos])); + } + return signals; +} + +zs::fd_t zs::fd_signaler_t::get_fd () +{ + return r; +} + +#endif + +#if defined ZS_HAVE_OPENVMS + +int zs::fd_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; + + zs_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 = 0x0100007f; + lcladdr.sin_port = INADDR_ANY; + + 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/fd_signaler.hpp b/src/fd_signaler.hpp new file mode 100644 index 0000000..34c5e8c --- /dev/null +++ b/src/fd_signaler.hpp @@ -0,0 +1,92 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_FD_SIGNALER_HPP_INCLUDED__ +#define __ZS_FD_SIGNALER_HPP_INCLUDED__ + +#include "platform.hpp" +#include "i_signaler.hpp" +#include "fd.hpp" +#include "stdint.hpp" + +namespace zs +{ + + // This object can be used to send individual signals from one thread to + // another. The specific of this pipe is that it has associated file + // descriptor and so it can be polled on. Same signal cannot be sent twice + // unless signals are retrieved by the reader side in the meantime. + + class fd_signaler_t : public i_signaler + { + public: + + typedef uint64_t signals_t; + + // Initialise the object. + fd_signaler_t (); + + // Destroy the object. + ~fd_signaler_t (); + + // Send specific signal. + void signal (int signal_); + + // Retrieves signals. Returns a set of signals in form of a bitmap. + // Signal with index 0 corresponds to value 1, index 1 to value 2, + // index 2 to value 4 etc. If there is no signal available, + // it returns zero immediately. + signals_t check (); + + // Get the file descriptor associated with the object. + fd_t get_fd (); + + 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 + +#if defined ZS_HAVE_EVENTFD + // Eventfd descriptor. + fd_t fd; +#else + // Write & read end of the socketpair. + fd_t w; + fd_t r; +#endif + + // Disable copying of fd_signeler object. + fd_signaler_t (const fd_signaler_t&); + void operator = (const fd_signaler_t&); + }; + +} + +#endif diff --git a/src/i_api.hpp b/src/i_api.hpp new file mode 100644 index 0000000..4dccd9e --- /dev/null +++ b/src/i_api.hpp @@ -0,0 +1,39 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_API_HPP_INCLUDED__ +#define __ZS_I_API_HPP_INCLUDED__ + +namespace zs +{ + + struct i_api + { + virtual int bind (const char *addr_, struct zs_opts *opts_) = 0; + virtual int connect (const char *addr_, struct zs_opts *opts_) = 0; + virtual int subscribe (const char *criteria_) = 0; + virtual int send (struct zs_msg *msg_, int flags_) = 0; + virtual int flush () = 0; + virtual int recv (struct zs_msg *msg_, int flags_) = 0; + virtual int close () = 0; + }; + +} + +#endif diff --git a/src/i_demux.hpp b/src/i_demux.hpp new file mode 100644 index 0000000..edded1e --- /dev/null +++ b/src/i_demux.hpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_DEMUX_HPP_INCLUDED__ +#define __ZS_I_DEMUX_HPP_INCLUDED__ + +namespace zs +{ + + struct i_demux + { + // Attaches mux to a particular session. + virtual void set_session (class session_t *session_) = 0; + + // To be called when the whole infrastrucure is being closed (zs_term). + virtual void shutdown () = 0; + + // To be called when session is being closed. + virtual void terminate () = 0; + + // Adds new pipe to the demux to send messages to. + virtual void attach_pipe (class pipe_writer_t *pipe_) = 0; + + // Removes pipe from the demux. + virtual void detach_pipe (class pipe_writer_t *pipe_) = 0; + + // Returns true if there's no pipe attached. + virtual bool empty () = 0; + + // Sends the message. Returns false if the message cannot be sent + // because the pipes are full. + virtual bool send (struct zs_msg *msg_) = 0; + + // Flushes messages downstream. + virtual void flush () = 0; + }; + +} + +#endif diff --git a/src/i_engine.hpp b/src/i_engine.hpp new file mode 100644 index 0000000..bade705 --- /dev/null +++ b/src/i_engine.hpp @@ -0,0 +1,53 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_ENGINE_HPP_INCLUDED__ +#define __ZS_I_ENGINE_HPP_INCLUDED__ + +namespace zs +{ + + // Generic interface to access engines from MD objects. + + struct i_engine + { + // Attach the engine with specified context. + virtual void attach (struct i_poller *poller_, + struct i_session *session_) = 0; + + // Detach the engine from the current context. + virtual void detach () = 0; + + // Notify the engine that new messages are available. + virtual void revive () = 0; + + // Called by session when it decides the engine + // should terminate itself. + virtual void schedule_terminate () = 0; + + // Called by normal object termination process. + virtual void terminate () = 0; + + // To be called by MD when terminal shutdown (zs_term) is in progress. + virtual void shutdown () = 0; + }; + +} + +#endif diff --git a/src/i_mux.hpp b/src/i_mux.hpp new file mode 100644 index 0000000..4c8ef72 --- /dev/null +++ b/src/i_mux.hpp @@ -0,0 +1,59 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_MUX_HPP_INCLUDED__ +#define __ZS_I_MUX_HPP_INCLUDED__ + +namespace zs +{ + + struct i_mux + { + // Attaches mux to a particular session. + virtual void set_session (class session_t *session_) = 0; + + // To be called when the whole infrastrucure is being closed (zs_term). + virtual void shutdown () = 0; + + // To be called when session is being closed. + virtual void terminate () = 0; + + // Adds new pipe to the mux to send messages to. + virtual void attach_pipe (class pipe_reader_t *pipe_) = 0; + + // Removes pipe from the mux. + virtual void detach_pipe (class pipe_reader_t *pipe_) = 0; + + // Returns true if there's no pipe attached. + virtual bool empty () = 0; + + // Shifts the pipe from active to passive state and vice versa. + // TODO: Check whether state transitions cannot be done by + // mux object itself without a need for external APIs. + virtual void deactivate (class pipe_reader_t *pipe_) = 0; + virtual void reactivate (class pipe_reader_t *pipe_) = 0; + + // Receives a message. Returns false when there is no message + // to receive. + virtual bool recv (struct zs_msg *msg_) = 0; + }; + +} + +#endif diff --git a/src/i_poll_events.hpp b/src/i_poll_events.hpp new file mode 100644 index 0000000..5189dad --- /dev/null +++ b/src/i_poll_events.hpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_POLL_EVENTS_HPP_INCLUDED__ +#define __ZS_I_POLL_EVENTS_HPP_INCLUDED__ + +namespace zs +{ + + // 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/i_poller.hpp b/src/i_poller.hpp new file mode 100644 index 0000000..c226dfa --- /dev/null +++ b/src/i_poller.hpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_POLLER_HPP_INCLUDED__ +#define __ZS_I_POLLER_HPP_INCLUDED__ + +#include "fd.hpp" + +namespace zs +{ + + union handle_t + { + fd_t fd; + void *ptr; + }; + + // Virtual interface to be used when polling on file descriptors. + + struct i_poller + { + virtual ~i_poller () {}; + + // Add file descriptor to the polling set. Return handle + // representing the descriptor. 'events' interface will be used + // to invoke callback functions when event occurs. + virtual handle_t add_fd (fd_t fd_, struct i_poll_events *events_) = 0; + + // Remove file descriptor identified by handle from the polling set. + virtual void rm_fd (handle_t handle_) = 0; + + // Start polling for input from socket. + virtual void set_pollin (handle_t handle_) = 0; + + // Stop polling for input from socket. + virtual void reset_pollin (handle_t handle_) = 0; + + // Start polling for availability of the socket for writing. + virtual void set_pollout (handle_t handle_) = 0; + + // Stop polling for availability of the socket for writing. + virtual void reset_pollout (handle_t handle_) = 0; + + // Ask to be notified after some time. Actual interval varies between + // 0 and max_timer_period ms. Timer is destroyed once it expires or, + // optionally, when cancel_timer is called. + virtual void add_timer (struct i_poll_events *events_) = 0; + + // Cancel the timer set by add_timer method. + virtual void cancel_timer (struct i_poll_events *events_) = 0; + + // Returns load experienced by the I/O thread. Currently it's number + // of file descriptors handled by the poller, in the future we may + // use a metric taking actual traffic on the individual sockets into + // account. + virtual int get_load () = 0; + + // Start the execution of the underlying I/O thread. + // This method is called from a foreign thread. + virtual void start () = 0; + + // Ask underlying I/O thread to stop. This method is called from + // underlying thread (callback from io_thread object). + virtual void stop () = 0; + + // Wait for termination of undelying I/O thread. + // This method is called from a foreign thread. + virtual void join () = 0; + }; + +} + +#endif diff --git a/src/i_session.hpp b/src/i_session.hpp new file mode 100644 index 0000000..8a8c40f --- /dev/null +++ b/src/i_session.hpp @@ -0,0 +1,37 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_SESSION_HPP_INCLUDED__ +#define __ZS_I_SESSION_HPP_INCLUDED__ + +namespace zs +{ + + struct i_session + { + virtual void set_engine (struct i_engine *engine_) = 0; + virtual void shutdown () = 0; + virtual bool read (struct zs_msg *msg_) = 0; + virtual bool write (struct zs_msg *msg_) = 0; + virtual void flush () = 0; + }; + +} + +#endif diff --git a/src/i_signaler.hpp b/src/i_signaler.hpp new file mode 100644 index 0000000..f6f0398 --- /dev/null +++ b/src/i_signaler.hpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_SIGNALER_HPP_INCLUDED__ +#define __ZS_I_SIGNALER_HPP_INCLUDED__ + +namespace zs +{ + // Virtual interface used to send signals. Individual implementations + // may restrict the number of possible signal types to send. + + struct i_signaler + { + virtual ~i_signaler () {}; + + // Send a signal with a specific ID. + virtual void signal (int signal_) = 0; + }; + +} + +#endif diff --git a/src/i_thread.hpp b/src/i_thread.hpp new file mode 100644 index 0000000..fdb60c5 --- /dev/null +++ b/src/i_thread.hpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_I_THREAD_HPP_INCLUDED__ +#define __ZS_I_THREAD_HPP_INCLUDED__ + +namespace zs +{ + + // Interface used by session object to communicate with the thread + // it belongs to. + + struct i_thread + { + virtual void attach_session (class session_t *session_) = 0; + virtual void detach_session (class session_t *session_) = 0; + virtual struct i_poller *get_poller () = 0; + }; + +} + +#endif diff --git a/src/io_object.cpp b/src/io_object.cpp new file mode 100644 index 0000000..01388eb --- /dev/null +++ b/src/io_object.cpp @@ -0,0 +1,37 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "io_object.hpp" +#include "io_thread.hpp" +#include "i_poller.hpp" + +zs::io_object_t::io_object_t (io_thread_t *thread_) : + object_t (thread_), + thread (thread_) +{ +} + +zs::io_object_t::~io_object_t () +{ +} + +zs::i_poller *zs::io_object_t::get_poller () +{ + return thread->get_poller (); +} diff --git a/src/io_object.hpp b/src/io_object.hpp new file mode 100644 index 0000000..766c008 --- /dev/null +++ b/src/io_object.hpp @@ -0,0 +1,51 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_IO_OBJECT_HPP_INCLUDED__ +#define __ZS_IO_OBJECT_HPP_INCLUDED__ + +#include "object.hpp" + +namespace zs +{ + + // All objects running within the context of an I/O thread should be + // derived from this class to allow owning application threads to + // destroy them. + + class io_object_t : public object_t + { + public: + + io_object_t (class io_thread_t *thread_); + ~io_object_t (); + + virtual void terminate () = 0; + virtual void shutdown () = 0; + + struct i_poller *get_poller (); + + private: + + class io_thread_t *thread; + }; + +} + +#endif diff --git a/src/io_thread.cpp b/src/io_thread.cpp new file mode 100644 index 0000000..7994874 --- /dev/null +++ b/src/io_thread.cpp @@ -0,0 +1,177 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "io_thread.hpp" +#include "command.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "command.hpp" +#include "epoll.hpp" +#include "poll.hpp" +#include "select.hpp" +#include "devpoll.hpp" +#include "kqueue.hpp" +#include "dispatcher.hpp" +#include "session.hpp" +#include "simple_semaphore.hpp" +#include "session.hpp" + +zs::io_thread_t::io_thread_t (dispatcher_t *dispatcher_, int thread_slot_) : + object_t (dispatcher_, thread_slot_) +{ +#if defined ZS_FORCE_SELECT + poller = new select_t; +#elif defined ZS_FORCE_POLL + poller = new poll_t; +#elif defined ZS_FORCE_EPOLL + poller = new epoll_t; +#elif defined ZS_FORCE_DEVPOLL + poller = new devpoll_t; +#elif defined ZS_FORCE_KQUEUE + poller = new kqueue_t; +#elif defined ZS_HAVE_LINUX + poller = new epoll_t; +#elif defined ZS_HAVE_WINDOWS + poller = new select_t; +#elif defined ZS_HAVE_FREEBSD + poller = new kqueue_t; +#elif defined ZS_HAVE_OPENBSD + poller = new kqueue_t; +#elif defined ZS_HAVE_SOLARIS + poller = new devpoll_t; +#elif defined ZS_HAVE_OSX + poller = new kqueue_t; +#elif defined ZS_HAVE_QNXNTO + poller = new poll_t; +#elif defined ZS_HAVE_AIX + poller = new poll_t; +#elif defined ZS_HAVE_HPUX + poller = new devpoll_t; +#elif defined ZS_HAVE_OPENVMS + poller = new select_t; +#else +#error Unsupported platform +#endif + zs_assert (poller); + + signaler_handle = poller->add_fd (signaler.get_fd (), this); + poller->set_pollin (signaler_handle); +} + +void zs::io_thread_t::shutdown () +{ + // Deallocate all the sessions associated with the thread. + while (!sessions.empty ()) + sessions [0]->shutdown (); + + delete this; +} + +zs::io_thread_t::~io_thread_t () +{ + delete poller; +} + +void zs::io_thread_t::start () +{ + // Start the underlying I/O thread. + poller->start (); +} + +void zs::io_thread_t::stop () +{ + send_stop (); +} + +void zs::io_thread_t::join () +{ + poller->join (); +} + +zs::i_signaler *zs::io_thread_t::get_signaler () +{ + return &signaler; +} + +int zs::io_thread_t::get_load () +{ + return poller->get_load (); +} + +void zs::io_thread_t::in_event () +{ + // Find out which threads are sending us commands. + fd_signaler_t::signals_t signals = signaler.check (); + zs_assert (signals); + + // Iterate through all the threads in the process and find out + // which of them sent us commands. + int slot_count = thread_slot_count (); + for (int source_thread_slot = 0; + source_thread_slot != slot_count; source_thread_slot++) { + if (signals & (fd_signaler_t::signals_t (1) << source_thread_slot)) { + + // Read all the commands from particular thread. + command_t cmd; + while (dispatcher->read (source_thread_slot, thread_slot, &cmd)) + cmd.destination->process_command (cmd); + } + } +} + +void zs::io_thread_t::out_event () +{ + // We are never polling for POLLOUT here. This function is never called. + zs_assert (false); +} + +void zs::io_thread_t::timer_event () +{ + // No timers here. This function is never called. + zs_assert (false); +} + +void zs::io_thread_t::attach_session (session_t *session_) +{ + session_->set_index (sessions.size ()); + sessions.push_back (session_); +} + +void zs::io_thread_t::detach_session (session_t *session_) +{ + // O(1) removal of the session from the list. + sessions_t::size_type i = session_->get_index (); + sessions [i] = sessions [sessions.size () - 1]; + sessions [i]->set_index (i); + sessions.pop_back (); +} + +zs::i_poller *zs::io_thread_t::get_poller () +{ + zs_assert (poller); + return poller; +} + +void zs::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..a57aa34 --- /dev/null +++ b/src/io_thread.hpp @@ -0,0 +1,99 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_IO_THREAD_HPP_INCLUDED__ +#define __ZS_IO_THREAD_HPP_INCLUDED__ + +#include + +#include "object.hpp" +#include "i_thread.hpp" +#include "i_poller.hpp" +#include "i_poll_events.hpp" +#include "fd_signaler.hpp" + +namespace zs +{ + + // 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 i_thread + { + public: + + io_thread_t (class dispatcher_t *dispatcher_, int thread_slot_); + + // Launch the physical thread. + void start (); + + // Ask underlying thread to stop. + void stop (); + + // Wait till undelying thread terminates. + void join (); + + // To be called when the whole infrastrucure is being closed (zs_term). + // It's vital to call the individual commands in this sequence: + // stop, join, shutdown. + void shutdown (); + + // Returns signaler associated with this I/O thread. + i_signaler *get_signaler (); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (); + + // i_thread implementation. + void attach_session (class session_t *session_); + void detach_session (class session_t *session_); + struct i_poller *get_poller (); + + // Command handlers. + void process_stop (); + + // Returns load experienced by the I/O thread. + int get_load (); + + private: + + // Clean-up. + ~io_thread_t (); + + // Poll thread gets notifications about incoming commands using + // this signaler. + fd_signaler_t signaler; + + // Handle associated with signaler's file descriptor. + handle_t signaler_handle; + + // I/O multiplexing is performed using a poller object. + i_poller *poller; + + // Vector of all sessions associated with this app thread. + typedef std::vector sessions_t; + sessions_t sessions; + + }; + +} + +#endif diff --git a/src/ip.cpp b/src/ip.cpp new file mode 100644 index 0000000..f435bef --- /dev/null +++ b/src/ip.cpp @@ -0,0 +1,310 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include +#include +#include +#include + +#include "ip.hpp" +#include "platform.hpp" +#include "err.hpp" +#include "stdint.hpp" + +#if defined ZS_HAVE_SOLARIS + +#include +#include +#include + +// On Solaris platform, network interface name can be queried by ioctl. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // * resolves to INADDR_ANY + if (!interface_ || (strlen (interface_) == 1 && *interface_ == '*')) { + addr_->s_addr = htonl (INADDR_ANY); + return 0; + } + + // Create a socket. + int fd = socket (AF_INET, SOCK_DGRAM, 0); + zs_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); + zs_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); + zs_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); + zs_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 interface was not found among interface names, we assume it's + // specified in the form of IP address. + if (!found) { + rc = inet_pton (AF_INET, interface_, addr_); + if (rc != 1) { + errno = EINVAL; + return -1; + } + } + + return 0; +} + +#elif defined ZS_HAVE_AIX || ZS_HAVE_HPUX + +#include +#include +#include +#include + +#include "formatting.hpp" + +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // * resolves to INADDR_ANY + if (!interface_ || (strlen (interface_) == 1 && *interface_ == '*')) { + addr_->s_addr = htonl (INADDR_ANY); + return 0; + } + + // Create a socket. + int sd = socket (AF_INET, SOCK_DGRAM, 0); + zs_assert (sd != -1); + + struct ifreq ifr; + + // Copy interface name for ioctl get. + zs_strncpy (ifr.ifr_name, interface_, sizeof (ifr.ifr_name)); + + // Fetch interface address. + int rc = ioctl (sd, SIOCGIFADDR, (caddr_t) &ifr, sizeof (struct ifreq)); + + if(rc != -1) { + struct sockaddr *sa = (struct sockaddr *) &ifr.ifr_addr; + *addr_ = ((sockaddr_in*)sa)->sin_addr; + } + else { + + // Assume interface_ is in IP format xxx.xxx.xxx.xxx. + rc = inet_pton (AF_INET, interface_, addr_); + if (rc != 0) { + errno = EINVAL; + return -1; + } + } + + // Clean up. + close (sd); + + return 0; +} + +#elif defined ZS_HAVE_WINDOWS + +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // * resolves to INADDR_ANY + if (!interface_ || (strlen (interface_) == 1 && *interface_ == '*')) { + addr_->s_addr = htonl (INADDR_ANY); + return 0; + } + + // Windows doesn't use sensible NIC names. Thus, we expect IP address of + // the NIC instead. + in_addr addr; + ((sockaddr_in*) addr_)->sin_family = AF_INET; + addr.S_un.S_addr = inet_addr ((const char *) interface_); + if (addr.S_un.S_addr == INADDR_NONE) { + errno = EINVAL; + return -1; + } + *addr_ = addr; + + return 0; +} + +#elif ((defined ZS_HAVE_LINUX || defined ZS_HAVE_FREEBSD ||\ + defined ZS_HAVE_OSX || defined ZS_HAVE_OPENBSD ||\ + defined ZS_HAVE_QNXNTO) && defined ZS_HAVE_IFADDRS) + +#include + +// On these platforms, network interface name can be queried +// using getifaddrs function. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // * resolves to INADDR_ANY + if (!interface_ || (strlen (interface_) == 1 && *interface_ == '*')) { + addr_->s_addr = htonl (INADDR_ANY); + return 0; + } + + // Initialuse the output parameter. + memset (addr_, 0, sizeof (in_addr)); + + // Get the addresses. + ifaddrs* ifa = NULL; + int rc = getifaddrs (&ifa); + zs_assert (rc == 0); + zs_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; + } + + // Clean-up; + freeifaddrs (ifa); + + // If interface was not found among interface names, we assume it's + // specified in the form of IP address. + if (!found) { + rc = inet_pton (AF_INET, interface_, addr_); + if (rc != 1) { + errno = EINVAL; + return -1; + } + } + + return 0; +} + +#else + +// On other platforms interface name is interpreted as raw IP address. +static int resolve_nic_name (in_addr* addr_, char const *interface_) +{ + // * resolves to INADDR_ANY + if (!interface_ || (strlen (interface_) == 1 && *interface_ == '*')) { + addr_->s_addr = htonl (INADDR_ANY); + return 0; + } + + // Convert IP address into sockaddr_in structure. + int rc = inet_pton (AF_INET, interface_, addr_); + zs_assert (rc != 0); + errno_assert (rc == 1); + + return 0; +} + +#endif + +int zs::resolve_ip_interface (sockaddr_in* addr_, char const *interface_) +{ + // Find the ':' that separates NIC name from port. + const char *delimiter = strchr (interface_, ':'); + if (!delimiter) { + errno = EINVAL; + return -1; + } + + // Clean the structure and fill in protocol family. + memset (addr_, 0, sizeof (sockaddr_in)); + addr_->sin_family = AF_INET; + + // Resolve the name of the NIC. + std::string nic_name (interface_, delimiter - interface_); + if (resolve_nic_name (&addr_->sin_addr, nic_name.c_str ()) != 0) + return -1; + + // Resolve the port. + addr_->sin_port = htons ((uint16_t) atoi (delimiter + 1)); + if (!addr_->sin_port) { + errno = EINVAL; + return 0; + } + + return 0; +} + +int zs::resolve_ip_hostname (sockaddr_in *addr_, const char *hostname_) +{ + // Find the ':' that separates hostname name from port. + const char *delimiter = strchr (hostname_, ':'); + if (!delimiter) { + errno = EINVAL; + return -1; + } + + // Separate the hostname. + std::string hostname (hostname_, delimiter - hostname_); + + // Resolve host name. + addrinfo req; + memset (&req, 0, sizeof (req)); + req.ai_family = AF_INET; + addrinfo *res; + int rc = getaddrinfo (hostname.c_str (), NULL, &req, &res); + if (rc) { + errno = EINVAL; + return -1; + } + zs_assert (res->ai_addr->sa_family == AF_INET); + memcpy (addr_, res->ai_addr, sizeof (sockaddr_in)); + freeaddrinfo (res); + + // Fill in the port number. + addr_->sin_port = htons ((uint16_t) atoi (delimiter + 1)); + if (!addr_->sin_port) { + errno = EINVAL; + return -1; + } + + return 0; +} diff --git a/src/ip.hpp b/src/ip.hpp new file mode 100644 index 0000000..90f02e3 --- /dev/null +++ b/src/ip.hpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_IP_HPP_INCLUDED__ +#define __ZS_IP_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#include +#include +#include +#include +#endif + +namespace zs +{ + + // Resolves network interface name in : format. Symbol "*" + // (asterisk) resolves to INADDR_ANY (all network interfaces). + int resolve_ip_interface (sockaddr_in* addr_, char const *interface_); + + // This function resolves a string in : format. + // Hostname can be either the name of the host or its IP address. + int resolve_ip_hostname (sockaddr_in *addr_, const char *hostname_); +} + +#endif diff --git a/src/kqueue.cpp b/src/kqueue.cpp new file mode 100644 index 0000000..b0c23ee --- /dev/null +++ b/src/kqueue.cpp @@ -0,0 +1,214 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" + +#if defined ZS_HAVE_FREEBSD || defined ZS_HAVE_OPENBSD || defined ZS_HAVE_OSX + +#include +#include +#include +#include +#include +#include + +#include "kqueue.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zs::kqueue_t::kqueue_t () +{ + // Create event queue + kqueue_fd = kqueue (); + errno_assert (kqueue_fd != -1); +} + +zs::kqueue_t::~kqueue_t () +{ + close (kqueue_fd); +} + +void zs::kqueue_t::kevent_add (fd_t fd_, short filter_, void *udata_) +{ + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_ADD, 0, 0, udata_); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +void zs::kqueue_t::kevent_delete (fd_t fd_, short filter_) +{ + struct kevent ev; + + EV_SET (&ev, fd_, filter_, EV_DELETE, 0, 0, NULL); + int rc = kevent (kqueue_fd, &ev, 1, NULL, 0, NULL); + errno_assert (rc != -1); +} + +zs::handle_t zs::kqueue_t::add_fd (fd_t fd_, i_poll_events *reactor_) +{ + poll_entry_t *pe = new poll_entry_t; + zs_assert (pe != NULL); + + pe->fd = fd_; + pe->flag_pollin = 0; + pe->flag_pollout = 0; + pe->reactor = reactor_; + + handle_t handle; + handle.ptr = pe; + return handle; +} + +void zs::kqueue_t::rm_fd (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + 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 zs::kqueue_t::set_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->flag_pollin = true; + kevent_add (pe->fd, EVFILT_READ, pe); +} + +void zs::kqueue_t::reset_pollin (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->flag_pollin = false; + kevent_delete (pe->fd, EVFILT_READ); +} + +void zs::kqueue_t::set_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->flag_pollout = true; + kevent_add (pe->fd, EVFILT_WRITE, pe); +} + +void zs::kqueue_t::reset_pollout (handle_t handle_) +{ + poll_entry_t *pe = (poll_entry_t*) handle_.ptr; + pe->flag_pollout = false; + kevent_delete (pe->fd, EVFILT_WRITE); +} + +void zs::kqueue_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zs::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 zs::kqueue_t::get_load () +{ + return load.get (); +} + +void zs::kqueue_t::start () +{ + worker.start (worker_routine, this); +} + +void zs::kqueue_t::stop () +{ + stopping = true; +} + +void zs::kqueue_t::join () +{ + worker.stop (); +} + +void zs::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 zs::kqueue_t::worker_routine (void *arg_) +{ + ((kqueue_t*) arg_)->loop (); +} + +#endif diff --git a/src/kqueue.hpp b/src/kqueue.hpp new file mode 100644 index 0000000..f060b28 --- /dev/null +++ b/src/kqueue.hpp @@ -0,0 +1,112 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_KQUEUE_HPP_INCLUDED__ +#define __ZS_KQUEUE_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZS_HAVE_FREEBSD || defined ZS_HAVE_OPENBSD || defined ZS_HAVE_OSX + +#include + +#include "i_poller.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Implements socket polling mechanism using the BSD-specific + // kqueue interface. + + class kqueue_t : public i_poller + { + public: + + kqueue_t (); + virtual ~kqueue_t (); + + // i_poller implementation. + handle_t add_fd (fd_t fd_, 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 (i_poll_events *events_); + void cancel_timer (i_poll_events *events_); + int get_load (); + void start (); + void stop (); + void join (); + + 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 retired_t; + retired_t retired; + + // List of all the engines waiting for the timer event. + typedef std::vector 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/listener.cpp b/src/listener.cpp new file mode 100644 index 0000000..ae4a80f --- /dev/null +++ b/src/listener.cpp @@ -0,0 +1,170 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "listener.hpp" +#include "simple_semaphore.hpp" +#include "zmq_tcp_engine.hpp" +#include "io_thread.hpp" +#include "session_stub.hpp" +#include "session.hpp" +#include "err.hpp" +#include "dummy_aggregator.hpp" +#include "dummy_distributor.hpp" + +zs::listener_t::listener_t (io_thread_t *thread_, const char *addr_, + session_t *peer_, bool has_in_, bool has_out_, uint64_t taskset_) : + io_object_t (thread_), + poller (NULL), + addr (addr_), + peer (peer_), + taskset (taskset_), + has_in (has_in_), + has_out (has_out_) +{ +} + +void zs::listener_t::terminate () +{ + for (session_stubs_t::size_type i = 0; i != session_stubs.size (); i++) + session_stubs [i]->terminate (); + delete this; +} + +void zs::listener_t::shutdown () +{ + for (session_stubs_t::size_type i = 0; i != session_stubs.size (); i++) + session_stubs [i]->shutdown (); + delete this; +} + +zs::listener_t::~listener_t () +{ +} + +void zs::listener_t::got_identity (session_stub_t *session_stub_, + const char *identity_) +{ + // Get the engine allready disconnected from the stub and poller. + i_engine *engine = session_stub_->detach_engine (); + zs_assert (engine); + + // Find the corresponding session. + session_t *session; + sessions_t::iterator it = sessions.find (identity_); + + // Destroy the stub. + int i = session_stub_->get_index (); + session_stubs [i] = session_stubs [session_stubs.size () - 1]; + session_stubs [i]->set_index (i); + session_stubs.pop_back (); + session_stub_->terminate (); + + // If there's no session with the specified identity, create one. + if (it != sessions.end ()) { + session = it->second; + session->inc_seqnum (); + } + else { + + // Choose an I/O thread with the least load to handle the new session. + io_thread_t *io_thread = choose_io_thread (taskset); + + // Create the session and bind it to the I/O thread and peer. Make + // sure that the peer session won't get deallocated till it processes + // the subsequent bind command. + i_mux *mux = new dummy_aggregator_t; + zs_assert (mux); + i_demux *demux = new dummy_distributor_t; + zs_assert (demux); + session = new session_t (io_thread, io_thread, mux, demux, false, true); + zs_assert (session); + session->inc_seqnum (); + session->inc_seqnum (); + peer->inc_seqnum (); + send_reg_and_bind (session, peer, has_in, has_out); + } + + // Attach the engine to the session. + send_engine (session, engine); +} + +void zs::listener_t::process_reg (simple_semaphore_t *smph_) +{ + zs_assert (!poller); + poller = get_poller (); + + // Open the listening socket. + int rc = tcp_listener.open (addr.c_str ()); + zs_assert (rc == 0); + + // Unlock the application thread that created the listener. + if (smph_) + smph_->post (); + + // Start polling for incoming connections. + handle = poller->add_fd (tcp_listener.get_fd (), this); + poller->set_pollin (handle); +} + +void zs::listener_t::process_unreg (simple_semaphore_t *smph_) +{ + // Disassociate listener from the poller. + zs_assert (poller); + poller->rm_fd (handle); + poller = NULL; + + // Unlock the application thread closing the listener. + if (smph_) + smph_->post (); +} + +void zs::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 session stub for the engine to take care for it till its + // identity is retreived. + session_stub_t *session_stub = new session_stub_t (this); + zs_assert (session_stub); + session_stub->set_index (session_stubs.size ()); + session_stubs.push_back (session_stub); + + // Create an engine to encaspulate the socket. Engine will register itself + // with the stub so the stub will be able to free it in case of shutdown. + zmq_tcp_engine_t *engine = new zmq_tcp_engine_t (fd); + zs_assert (engine); + engine->attach (poller, session_stub); +} + +void zs::listener_t::out_event () +{ + zs_assert (false); +} + +void zs::listener_t::timer_event () +{ + zs_assert (false); +} + + diff --git a/src/listener.hpp b/src/listener.hpp new file mode 100644 index 0000000..f3c745a --- /dev/null +++ b/src/listener.hpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_LISTENER_HPP_INCLUDED__ +#define __ZS_LISTENER_HPP_INCLUDED__ + +#include +#include +#include + +#include "io_object.hpp" +#include "tcp_listener.hpp" +#include "i_poller.hpp" +#include "i_poll_events.hpp" +#include "stdint.hpp" + +namespace zs +{ + + class listener_t : public io_object_t, public i_poll_events + { + public: + + listener_t (class io_thread_t *thread_, const char *addr_, + class session_t *peer_, bool has_in_, bool has_out_, + uint64_t taskset_); + + void terminate (); + void shutdown (); + + // This function is called by session stub once the identity + // is retrieved from the incoming connection. + void got_identity (class session_stub_t *session_stub_, + const char *identity_); + + void process_reg (class simple_semaphore_t *smph_); + void process_unreg (class simple_semaphore_t *smph_); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (); + + private: + + ~listener_t (); + + struct i_poller *poller; + + // Handle corresponding to the listening socket. + handle_t handle; + + // Actual listening socket. + tcp_listener_t tcp_listener; + + // Address to bind to. + std::string addr; + + // Peer session. All the newly created connections should bind to + // this session. + session_t *peer; + + // Taskset specifies which I/O threads are to be use to handle + // newly created connections (0 = all). + uint64_t taskset; + + // Sessions created by this listener are stored in this map. They are + // indexed by peer identities so that the same peer connects to the + // same session after reconnection. + // NB: Sessions are destroyed from other place and possibly later on, + // so no need to care about them during listener object termination. + typedef std::map sessions_t; + sessions_t sessions; + + // List of engines (bound to temorary session stubs) that we haven't + // retrieved the identity from so far. + typedef std::vector session_stubs_t; + session_stubs_t session_stubs; + + // If true, create inbound pipe when binding new connection + // to the peer. + bool has_in; + + // If true, create outbound pipe when binding new connection + // to the peer. + bool has_out; + + listener_t (const listener_t&); + void operator = (const listener_t&); + }; + +} + +#endif diff --git a/src/load_balancer.cpp b/src/load_balancer.cpp new file mode 100644 index 0000000..63dc15c --- /dev/null +++ b/src/load_balancer.cpp @@ -0,0 +1,130 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "load_balancer.hpp" +#include "pipe_writer.hpp" +#include "err.hpp" +#include "session.hpp" +#include "msg.hpp" + +zs::load_balancer_t::load_balancer_t () : + session (NULL), + current (0) +{ +} + +void zs::load_balancer_t::set_session (session_t *session_) +{ + zs_assert (!session); + session = session_; +} + +void zs::load_balancer_t::shutdown () +{ + // No need to deallocate pipes here. They'll be deallocated during the + // shutdown of the dispatcher. + delete this; +} + +void zs::load_balancer_t::terminate () +{ + // Pipe unregisters itself during the call to terminate, so the pipes + // list shinks by one in each iteration. + while (!pipes.empty ()) + pipes [0]->terminate (); + + delete this; +} + +zs::load_balancer_t::~load_balancer_t () +{ +} + +void zs::load_balancer_t::attach_pipe (pipe_writer_t *pipe_) +{ + // Associate demux with a new pipe. + pipe_->set_demux (this); + pipe_->set_index (pipes.size ()); + pipes.push_back (pipe_); +} + +void zs::load_balancer_t::detach_pipe (pipe_writer_t *pipe_) +{ + // Release the reference to the pipe. + int index = pipe_->get_index (); + pipe_->set_index (-1); + pipes [index] = pipes.back (); + pipes [index]->set_index (index); + pipes.pop_back (); +} + +bool zs::load_balancer_t::empty () +{ + return pipes.empty (); +} + +bool zs::load_balancer_t::send (zs_msg *msg_) +{ + // If there are no pipes, message cannot be sent. + if (pipes.size () == 0) + return false; + + // Find the first pipe that is ready to accept the message. + bool found = false; + for (pipes_t::size_type i = 0; !found && i < pipes.size (); i++) { +// if (pipes [current]->check_write (msg)) + found = true; +// else +// current = (current + 1) % pipes.size (); + } + + // Oops, no pipe is ready to accept the message. + if (!found) + return false; + + // Send the message to the selected pipe. + write_to_pipe (pipes [current], msg_); + current = (current + 1) % pipes.size (); + + // Detach the original message from the data buffer. + zs_msg_init (msg_); + + return true; +} + +void zs::load_balancer_t::flush () +{ + // Flush all pipes. If there's large number of pipes, it can be pretty + // inefficient (especially if there's new message only in a single pipe). + // Can it be improved? + for (pipes_t::iterator it = pipes.begin (); it != pipes.end (); it ++) + (*it)->flush (); +} + +void zs::load_balancer_t::write_to_pipe (class pipe_writer_t *pipe_, + struct zs_msg *msg_) +{ + if (!pipe_->write (msg_)) { + // TODO: Push gap notification to the pipe. + zs_assert (false); + } +} + diff --git a/src/load_balancer.hpp b/src/load_balancer.hpp new file mode 100644 index 0000000..9cdc68f --- /dev/null +++ b/src/load_balancer.hpp @@ -0,0 +1,73 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_LOAD_BALANCER_HPP_INCLUDED__ +#define __ZS_LOAD_BALANCER_HPP_INCLUDED__ + +#include + +#include + +namespace zs +{ + + // Object to distribute messages to outbound pipes. + + class load_balancer_t : public i_demux + { + public: + + load_balancer_t (); + + // i_demux implementation. + void set_session (class session_t *session_); + void shutdown (); + void terminate (); + void attach_pipe (class pipe_writer_t *pipe_); + void detach_pipe (class pipe_writer_t *pipe_); + bool empty (); + bool send (struct zs_msg *msg_); + void flush (); + + private: + + // Clean-up. + ~load_balancer_t (); + + // Reference to the owner session object. + class session_t *session; + + // Writes the message to the pipe if possible. If it isn't, writes + // a gap notification to the pipe. + void write_to_pipe (class pipe_writer_t *pipe_, struct zs_msg *msg_); + + // The list of outbound pipes. + typedef std::vector pipes_t; + pipes_t pipes; + + // Current pipe to write next message to. + pipes_t::size_type current; + + load_balancer_t (const load_balancer_t&); + void operator = (const load_balancer_t&); + }; + +} + +#endif diff --git a/src/msg.hpp b/src/msg.hpp new file mode 100644 index 0000000..f4f4d26 --- /dev/null +++ b/src/msg.hpp @@ -0,0 +1,49 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_MSG_HPP_INCLUDE__ +#define __ZS_MSG_HPP_INCLUDE__ + +#include + +#include "../include/zs.h" + +#include "atomic_counter.hpp" + +//namespace zs +//{ + + // Shared message buffer. Message data are either allocated in one + // continguous 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 zs_msg_content + { + void *data; + size_t size; + zs_free_fn *ffn; + zs::atomic_counter_t refcnt; + }; + +//} + +#endif diff --git a/src/mutex.hpp b/src/mutex.hpp new file mode 100644 index 0000000..a7f95da --- /dev/null +++ b/src/mutex.hpp @@ -0,0 +1,116 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_MUTEX_HPP_INCLUDED__ +#define __ZS_MUTEX_HPP_INCLUDED__ + +#include "platform.hpp" +#include "err.hpp" + +// Mutex class encapsulates OS mutex in a platform-independent way. + +#ifdef ZS_HAVE_WINDOWS + +#include "windows.hpp" + +namespace zs +{ + + 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 + +namespace zs +{ + + class mutex_t + { + public: + inline mutex_t () + { + int rc = pthread_mutex_init (&mutex, NULL); + errno_assert (rc == 0); + } + + inline ~mutex_t () + { + int rc = pthread_mutex_destroy (&mutex); + errno_assert (rc == 0); + } + + inline void lock () + { + int rc = pthread_mutex_lock (&mutex); + errno_assert (rc == 0); + } + + inline void unlock () + { + int rc = pthread_mutex_unlock (&mutex); + errno_assert (rc == 0); + } + + 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..8a154ae --- /dev/null +++ b/src/object.cpp @@ -0,0 +1,294 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "object.hpp" +#include "dispatcher.hpp" +#include "err.hpp" +#include "pipe_reader.hpp" +#include "pipe_writer.hpp" +#include "session.hpp" +#include "io_thread.hpp" +#include "simple_semaphore.hpp" +#include "i_engine.hpp" + +zs::object_t::object_t (dispatcher_t *dispatcher_, int thread_slot_) : + dispatcher (dispatcher_), + thread_slot (thread_slot_) +{ +} + +zs::object_t::object_t (object_t *parent_) : + dispatcher (parent_->dispatcher), + thread_slot (parent_->thread_slot) +{ +} + +zs::object_t::~object_t () +{ +} + +int zs::object_t::thread_slot_count () +{ + return dispatcher->thread_slot_count (); +} + +int zs::object_t::get_thread_slot () +{ + return thread_slot; +} + +void zs::object_t::process_command (command_t &cmd_) +{ + switch (cmd_.type) { + + case command_t::head: + process_head (cmd_.args.head.bytes); + break; + + case command_t::tail: + process_tail (cmd_.args.tail.bytes); + break; + + case command_t::engine: + process_engine (cmd_.args.engine.engine); + break; + + case command_t::bind: + process_bind (cmd_.args.bind.reader, cmd_.args.bind.peer); + break; + + case command_t::reg: + process_reg (cmd_.args.reg.smph); + break; + + case command_t::reg_and_bind: + process_reg_and_bind (cmd_.args.reg_and_bind.peer, + cmd_.args.reg_and_bind.flow_in, cmd_.args.reg_and_bind.flow_out); + break; + + case command_t::unreg: + process_unreg (cmd_.args.unreg.smph); + break; + + case command_t::terminate: + process_terminate (); + break; + + case command_t::terminate_ack: + process_terminate_ack (); + break; + + case command_t::stop: + process_stop (); + return; + + default: + zs_assert (false); + } +} + +void zs::object_t::create_pipe (object_t *reader_parent_, + object_t *writer_parent_, uint64_t hwm_, uint64_t lwm_, + pipe_reader_t **reader_, pipe_writer_t **writer_) +{ + dispatcher->create_pipe (reader_parent_, writer_parent_, hwm_, lwm_, + reader_, writer_); +} + +void zs::object_t::destroy_pipe (pipe_t *pipe_) +{ + dispatcher->destroy_pipe (pipe_); +} + +int zs::object_t::register_inproc_endpoint (const char *endpoint_, + session_t *session_) +{ + return dispatcher->register_inproc_endpoint (endpoint_, session_); +} + +zs::object_t *zs::object_t::get_inproc_endpoint (const char *endpoint_) +{ + return dispatcher->get_inproc_endpoint (endpoint_); +} + +void zs::object_t::unregister_inproc_endpoints (session_t *session_) +{ + dispatcher->unregister_inproc_endpoints (session_); +} + +zs::io_thread_t *zs::object_t::choose_io_thread (uint64_t taskset_) +{ + return dispatcher->choose_io_thread (taskset_); +} + +void zs::object_t::send_stop () +{ + // Send command goes always to the current object. To-self pipe is + // used exclusively for sending this command. + command_t cmd; + cmd.destination = this; + cmd.type = command_t::stop; + dispatcher->write (thread_slot, thread_slot, cmd); +} + +void zs::object_t::send_bind (object_t *destination_, pipe_reader_t *reader_, + session_t *peer_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::bind; + cmd.args.bind.reader = reader_; + cmd.args.bind.peer = peer_; + send_command (cmd); +} + +void zs::object_t::send_head (object_t *destination_, uint64_t bytes_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::head; + cmd.args.head.bytes = bytes_; + send_command (cmd); +} + +void zs::object_t::send_tail (object_t *destination_, uint64_t bytes_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::tail; + cmd.args.tail.bytes = bytes_; + send_command (cmd); +} + +void zs::object_t::send_reg (object_t *destination_, simple_semaphore_t *smph_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::reg; + cmd.args.reg.smph = smph_; + send_command (cmd); +} + +void zs::object_t::send_reg_and_bind (object_t *destination_, + session_t *peer_, bool flow_in_, bool flow_out_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::reg_and_bind; + cmd.args.reg_and_bind.peer = peer_; + cmd.args.reg_and_bind.flow_in = flow_in_; + cmd.args.reg_and_bind.flow_out = flow_out_; + send_command (cmd); +} + +void zs::object_t::send_unreg (object_t *destination_, + simple_semaphore_t *smph_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::unreg; + cmd.args.unreg.smph = smph_; + send_command (cmd); +} + +void zs::object_t::send_engine (object_t *destination_, i_engine *engine_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::engine; + cmd.args.engine.engine = engine_; + send_command (cmd); +} + +void zs::object_t::send_terminate (object_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::terminate; + send_command (cmd); +} + +void zs::object_t::send_terminate_ack (object_t *destination_) +{ + command_t cmd; + cmd.destination = destination_; + cmd.type = command_t::terminate_ack; + send_command (cmd); +} + +void zs::object_t::process_stop () +{ + zs_assert (false); +} + +void zs::object_t::process_bind (pipe_reader_t *reader_, session_t *peer_) +{ + zs_assert (false); +} + +void zs::object_t::process_head (uint64_t bytes_) +{ + zs_assert (false); +} + +void zs::object_t::process_tail (uint64_t bytes_) +{ + zs_assert (false); +} + +void zs::object_t::process_reg (simple_semaphore_t *smph_) +{ + zs_assert (false); +} + +void zs::object_t::process_reg_and_bind (session_t *session_, + bool flow_in_, bool flow_out_) +{ + zs_assert (false); +} + +void zs::object_t::process_unreg (simple_semaphore_t *smph_) +{ + zs_assert (false); +} + +void zs::object_t::process_engine (i_engine *engine_) +{ + zs_assert (false); +} + +void zs::object_t::process_terminate () +{ + zs_assert (false); +} + +void zs::object_t::process_terminate_ack () +{ + zs_assert (false); +} + +void zs::object_t::send_command (command_t &cmd_) +{ + int destination_thread_slot = cmd_.destination->get_thread_slot (); + if (destination_thread_slot == thread_slot) + cmd_.destination->process_command (cmd_); + else + dispatcher->write (thread_slot, destination_thread_slot, cmd_); +} + diff --git a/src/object.hpp b/src/object.hpp new file mode 100644 index 0000000..a4f93ae --- /dev/null +++ b/src/object.hpp @@ -0,0 +1,105 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_OBJECT_HPP_INCLUDED__ +#define __ZS_OBJECT_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zs +{ + + // Base class for all objects that participate in inter-thread + // communication. + + class object_t + { + public: + + object_t (class dispatcher_t *dispatcher_, int thread_slot_); + object_t (object_t *parent_); + ~object_t (); + + int get_thread_slot (); + void process_command (struct command_t &cmd_); + + protected: + + // Derived object can use following functions to interact with + // global repositories. See dispatcher.hpp for function details. + int thread_slot_count (); + void create_pipe (class object_t *reader_parent_, + class object_t *writer_parent_, uint64_t hwm_, uint64_t lwm_, + class pipe_reader_t **reader_, class pipe_writer_t **writer_); + void destroy_pipe (class pipe_t *pipe_); + int register_inproc_endpoint (const char *endpoint_, + class session_t *session_); + class object_t *get_inproc_endpoint (const char *endpoint_); + void unregister_inproc_endpoints (class session_t *session_); + 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_bind (object_t *destination_, class pipe_reader_t *reader_, + class session_t *peer_); + void send_head (object_t *destination_, uint64_t bytes_); + void send_tail (object_t *destination_, uint64_t bytes_); + void send_reg (object_t *destination_, + class simple_semaphore_t *smph_); + void send_reg_and_bind (object_t *destination_, class session_t *peer_, + bool flow_in_, bool flow_out_); + void send_unreg (object_t *destination_, + class simple_semaphore_t *smph_); + void send_engine (object_t *destination_, struct i_engine *engine_); + void send_terminate (object_t *destination_); + void send_terminate_ack (object_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_bind (class pipe_reader_t *reader_, + class session_t *peer_); + virtual void process_head (uint64_t bytes_); + virtual void process_tail (uint64_t bytes_); + virtual void process_reg (class simple_semaphore_t *smph_); + virtual void process_reg_and_bind (class session_t *peer_, + bool flow_in_, bool flow_out_); + virtual void process_unreg (class simple_semaphore_t *smph_); + virtual void process_engine (struct i_engine *engine_); + virtual void process_terminate (); + virtual void process_terminate_ack (); + + // Pointer to the root of the infrastructure. + class dispatcher_t *dispatcher; + + // Slot ID of the thread the object belongs to. + int thread_slot; + + private: + + void send_command (command_t &cmd_); + + object_t (const object_t&); + void operator = (const object_t&); + }; + +} + +#endif diff --git a/src/p2p.cpp b/src/p2p.cpp new file mode 100644 index 0000000..f15b663 --- /dev/null +++ b/src/p2p.cpp @@ -0,0 +1,29 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "p2p.hpp" +#include "app_thread.hpp" +#include "session.hpp" + +zs::p2p_t::p2p_t (app_thread_t *thread_, session_t *session_) : + socket_base_t (thread_, session_) +{ +} diff --git a/src/p2p.hpp b/src/p2p.hpp new file mode 100644 index 0000000..1032a61 --- /dev/null +++ b/src/p2p.hpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_P2P_HPP_INCLUDED__ +#define __ZS_P2P_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zs +{ + + class p2p_t : public socket_base_t + { + public: + + p2p_t (class app_thread_t *thread_, class session_t *session_); + + private: + + p2p_t (const p2p_t&); + void operator = (const p2p_t&); + }; + +} + +#endif diff --git a/src/pipe.cpp b/src/pipe.cpp new file mode 100644 index 0000000..26042ae --- /dev/null +++ b/src/pipe.cpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "pipe.hpp" + +zs::pipe_t::pipe_t () : + ypipe_t (false), + index (-1) +{ +} + +zs::pipe_t::~pipe_t () +{ + // Flush any outstanding messages to the pipe. + flush (); + + // Deallocate all the messages in the pipe. + zs_msg msg; + while (read (&msg)) + zs_msg_close (&msg); +} + +void zs::pipe_t::set_index (int index_) +{ + index = index_; +} + +int zs::pipe_t::get_index () +{ + return index; +} diff --git a/src/pipe.hpp b/src/pipe.hpp new file mode 100644 index 0000000..c0e722d --- /dev/null +++ b/src/pipe.hpp @@ -0,0 +1,57 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_PIPE_HPP_INCLUDED__ +#define __ZS_PIPE_HPP_INCLUDED__ + +#include "../include/zs.h" + +#include "ypipe.hpp" +#include "config.hpp" + +namespace zs +{ + + // Message pipe. A simple wrapper on top of ypipe. + + class pipe_t : public ypipe_t + { + // Dispatcher is a friend so that it can create & destroy the pipes. + // By making constructor & destructor private we are sure that nobody + // except dispatcher messes with pipes. + friend class dispatcher_t; + + private: + + pipe_t (); + ~pipe_t (); + + void set_index (int index_); + int get_index (); + + // Index of the pipe in dispatcher's array of pipes. + int index; + + pipe_t (const pipe_t&); + void operator = (const pipe_t&); + }; + +} + +#endif diff --git a/src/pipe_reader.cpp b/src/pipe_reader.cpp new file mode 100644 index 0000000..5585b92 --- /dev/null +++ b/src/pipe_reader.cpp @@ -0,0 +1,118 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "pipe_reader.hpp" +#include "pipe.hpp" +#include "err.hpp" +#include "i_mux.hpp" + +zs::pipe_reader_t::pipe_reader_t (object_t *parent_, pipe_t *pipe_, + uint64_t hwm_, uint64_t lwm_) : + object_t (parent_), + pipe (pipe_), + peer (NULL), + mux (NULL), + index (-1), + hwm (hwm_), + lwm (lwm_), + head (0), + tail (0), + last_sent_head (0) +{ +} + +void zs::pipe_reader_t::set_peer (object_t *peer_) +{ + peer = peer_; +} + +zs::pipe_reader_t::~pipe_reader_t () +{ +} + +void zs::pipe_reader_t::set_mux (i_mux *mux_) +{ + mux = mux_; +} + +void zs::pipe_reader_t::set_index (int index_) +{ + index = index_; +} + +int zs::pipe_reader_t::get_index () +{ + return index; +} + +void zs::pipe_reader_t::process_tail (uint64_t bytes_) +{ + tail = bytes_; + mux->reactivate (this); +} + +bool zs::pipe_reader_t::read (struct zs_msg *msg_) +{ + // Read a message. + if (!pipe->read (msg_)) { + mux->deactivate (this); + return false; + } + + // If successfull, adjust the head of the pipe. + head += zs_msg_size (msg_); + + // If pipe writer wasn't notified about the head position for long enough, + // notify it. + if (head - last_sent_head >= hwm - lwm) { + send_head (peer, head); + last_sent_head = head; + } + + if (zs_msg_type (msg_) == ZS_DELIMITER) { + + // Detach the pipe from the mux and send termination request to + // the pipe writer. + mux->detach_pipe (this); + mux = NULL; + send_terminate (peer); + return false; + } + + return true; +} + +void zs::pipe_reader_t::terminate () +{ + // Detach the pipe from the mux and send termination request to + // the pipe writer. + if (mux) { + mux->detach_pipe (this); + mux = NULL; + } + send_terminate (peer); +} + +void zs::pipe_reader_t::process_terminate_ack () +{ + // Ask dispatcher to deallocate the pipe. + destroy_pipe (pipe); +} diff --git a/src/pipe_reader.hpp b/src/pipe_reader.hpp new file mode 100644 index 0000000..bc3fd2e --- /dev/null +++ b/src/pipe_reader.hpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_PIPE_READER_HPP_INCLUDED__ +#define __ZS_PIPE_READER_HPP_INCLUDED__ + +#include "object.hpp" +#include "stdint.hpp" + +namespace zs +{ + + class pipe_reader_t : public object_t + { + // Dispatcher is a friend so that it can create & destroy the reader. + // By making constructor & destructor private we are sure that nobody + // except dispatcher messes with readers. + friend class dispatcher_t; + + public: + + // Set & get index in the associated mux object. + void set_mux (struct i_mux *mux_); + void set_index (int index_); + int get_index (); + + // Reads a message to the underlying pipe. + bool read (struct zs_msg *msg_); + + // Asks pipe to destroy itself. + void terminate (); + + private: + + pipe_reader_t (class object_t *parent_, class pipe_t *pipe_, + uint64_t hwm_, uint64_t lwm_); + ~pipe_reader_t (); + + // Second step of reader construction. The parameter cannot be passed + // in constructor as peer object doesn't yet exist at the time. + void set_peer (class object_t *peer_); + + void process_tail (uint64_t bytes_); + void process_terminate_ack (); + + // The underlying pipe. + class pipe_t *pipe; + + // Pipe writer associated with the other side of the pipe. + class object_t *peer; + + // Associated mux object. + struct i_mux *mux; + + // Index in the associated mux object. + int index; + + // High and low watermarks for in-memory storage (in bytes). + uint64_t hwm; + uint64_t lwm; + + // Positions of head and tail of the pipe (in bytes). + uint64_t head; + uint64_t tail; + uint64_t last_sent_head; + + pipe_reader_t (const pipe_reader_t&); + void operator = (const pipe_reader_t&); + }; + +} + +#endif diff --git a/src/pipe_writer.cpp b/src/pipe_writer.cpp new file mode 100644 index 0000000..173cf97 --- /dev/null +++ b/src/pipe_writer.cpp @@ -0,0 +1,120 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "pipe_writer.hpp" +#include "pipe.hpp" +#include "i_demux.hpp" + +zs::pipe_writer_t::pipe_writer_t (object_t *parent_, pipe_t *pipe_, + object_t *peer_, uint64_t hwm_, uint64_t lwm_) : + object_t (parent_), + pipe (pipe_), + peer (peer_), + demux (NULL), + index (-1), + hwm (hwm_), + lwm (lwm_), + head (0), + tail (0) +{ +} + +zs::pipe_writer_t::~pipe_writer_t () +{ +} + +void zs::pipe_writer_t::set_demux (i_demux *demux_) +{ + demux = demux_; +} + +void zs::pipe_writer_t::set_index (int index_) +{ + index = index_; +} + +int zs::pipe_writer_t::get_index () +{ + return index; +} + +bool zs::pipe_writer_t::write (zs_msg *msg_) +{ + size_t msg_size = zs_msg_size (msg_); + + // If message won't fit into the in-memory pipe, there's no way + // to pass it further. + // TODO: It should be discarded and 'oversized' notification should be + // placed into the pipe. + zs_assert (!hwm || msg_size <= hwm); + + // If there's not enough space in the pipe at the moment, return false. + if (hwm && tail + msg_size - head > hwm) + return false; + + // Write the message to the pipe and adjust tail position. + pipe->write (*msg_); + flush (); + tail += msg_size; + + return true; +} + +void zs::pipe_writer_t::flush () +{ + if (!pipe->flush ()) + send_tail (peer, tail); +} + +void zs::pipe_writer_t::process_head (uint64_t bytes_) +{ + head = bytes_; +} + +void zs::pipe_writer_t::terminate () +{ + // Disconnect from the associated demux. + if (demux) { + demux->detach_pipe (this); + demux = NULL; + } + + // Push the delimiter to the pipe. Delimiter is a notification for pipe + // reader that there will be no more messages in the pipe. + zs_msg delimiter; + delimiter.content = (zs_msg_content*) ZS_DELIMITER; + delimiter.shared = false; + delimiter.vsm_size = 0; + pipe->write (delimiter); + flush (); +} + +void zs::pipe_writer_t::process_terminate () +{ + // Disconnect from the associated demux. + if (demux) { + demux->detach_pipe (this); + demux = NULL; + } + + // Send termination acknowledgement to the pipe reader. + send_terminate_ack (peer); +} diff --git a/src/pipe_writer.hpp b/src/pipe_writer.hpp new file mode 100644 index 0000000..3b4b4cd --- /dev/null +++ b/src/pipe_writer.hpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_PIPE_WRITER_HPP_INCLUDED__ +#define __ZS_PIPE_WRITER_HPP_INCLUDED__ + +#include "object.hpp" +#include "stdint.hpp" + +namespace zs +{ + + class pipe_writer_t : public object_t + { + // Dispatcher is a friend so that it can create & destroy the writer. + // By making constructor & destructor private we are sure that nobody + // except dispatcher messes with writers. + friend class dispatcher_t; + + public: + + // Set & get index in the associated demux object. + void set_demux (struct i_demux *demux_); + void set_index (int index_); + int get_index (); + + // Writes a message to the underlying pipe. Returns false if the + // message cannot be written to the pipe at the moment. + bool write (struct zs_msg *msg_); + + // Flush the messages downsteam. + void flush (); + + // Asks pipe to destroy itself. + void terminate (); + + private: + + pipe_writer_t (class object_t *parent_, class pipe_t *pipe_, + class object_t *peer_, uint64_t hwm_, uint64_t lwm_); + ~pipe_writer_t (); + + void process_head (uint64_t bytes_); + void process_terminate (); + + // The underlying pipe. + class pipe_t *pipe; + + // Pipe reader associated with the other side of the pipe. + class object_t *peer; + + // Associated demux object. + struct i_demux *demux; + + // Index in the associated demux object. + int index; + + // High and low watermarks for in-memory storage (in bytes). + uint64_t hwm; + uint64_t lwm; + + // Positions of head and tail of the pipe (in bytes). + uint64_t head; + uint64_t tail; + + pipe_writer_t (const pipe_writer_t&); + void operator = (const pipe_writer_t&); + }; + +} + +#endif diff --git a/src/platform.hpp.in b/src/platform.hpp.in new file mode 100644 index 0000000..108d50d --- /dev/null +++ b/src/platform.hpp.in @@ -0,0 +1,210 @@ +/* src/platform.hpp.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_IFADDRS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* 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 `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 header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_NETINET_IN_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_EVENTFD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* 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 + +/* 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 and . */ +#undef TIME_WITH_SYS_TIME + +/* Version number of package */ +#undef VERSION + +/* Force to use mutexes */ +#undef ZS_FORCE_MUTEXES + +/* Have AIX OS */ +#undef ZS_HAVE_AIX + +/* Have eventfd extension. */ +#undef ZS_HAVE_EVENTFD + +/* Have FreeBSD OS */ +#undef ZS_HAVE_FREEBSD + +/* Have HPUX OS */ +#undef ZS_HAVE_HPUX + +/* Have ifaddrs.h header. */ +#undef ZS_HAVE_IFADDRS + +/* Have Linux OS */ +#undef ZS_HAVE_LINUX + +/* Have MinGW32 */ +#undef ZS_HAVE_MINGW32 + +/* Have OpenBSD OS */ +#undef ZS_HAVE_OPENBSD + +/* Have DarwinOSX OS */ +#undef ZS_HAVE_OSX + +/* Have QNX Neutrino OS */ +#undef ZS_HAVE_QNXNTO + +/* Have Solaris OS */ +#undef ZS_HAVE_SOLARIS + +/* Have Windows OS */ +#undef ZS_HAVE_WINDOWS + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or 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 rpl_malloc if the replacement function should be used. */ +#undef malloc + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if 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..59a0cd7 --- /dev/null +++ b/src/poll.cpp @@ -0,0 +1,205 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" + +#if defined ZS_HAVE_LINUX || defined ZS_HAVE_FREEBSD ||\ + defined ZS_HAVE_OPENBSD || defined ZS_HAVE_SOLARIS ||\ + defined ZS_HAVE_OSX || defined ZS_HAVE_QNXNTO ||\ + defined ZS_HAVE_HPUX || defined ZS_HAVE_AIX + +#include +#include +#include +#include +#include + +#include "poll.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zs::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; +} + +zs::handle_t zs::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); + + handle_t handle; + handle.fd = fd_; + return handle; +} + +void zs::poll_t::rm_fd (handle_t handle_) +{ + fd_t index = fd_table [handle_.fd].index; + assert (index != retired_fd); + + // Mark the fd as unused. + pollset [index].fd = retired_fd; + fd_table [handle_.fd].index = retired_fd; + retired = true; + + // Decrease the load metric of the thread. + load.sub (1); +} + +void zs::poll_t::set_pollin (handle_t handle_) +{ + int index = fd_table [handle_.fd].index; + pollset [index].events |= POLLIN; +} + +void zs::poll_t::reset_pollin (handle_t handle_) +{ + int index = fd_table [handle_.fd].index; + pollset [index].events &= ~((short) POLLIN); +} + +void zs::poll_t::set_pollout (handle_t handle_) +{ + int index = fd_table [handle_.fd].index; + pollset [index].events |= POLLOUT; +} + +void zs::poll_t::reset_pollout (handle_t handle_) +{ + int index = fd_table [handle_.fd].index; + pollset [index].events &= ~((short) POLLOUT); +} + +void zs::poll_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zs::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 zs::poll_t::get_load () +{ + return load.get (); +} + +void zs::poll_t::start () +{ + worker.start (worker_routine, this); +} + +void zs::poll_t::stop () +{ + stopping = true; +} + +void zs::poll_t::join () +{ + worker.stop (); +} + +void zs::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 ++) { + + zs_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 zs::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..65095f7 --- /dev/null +++ b/src/poll.hpp @@ -0,0 +1,112 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_POLL_HPP_INCLUDED__ +#define __ZS_POLL_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZS_HAVE_LINUX || defined ZS_HAVE_FREEBSD ||\ + defined ZS_HAVE_OPENBSD || defined ZS_HAVE_SOLARIS ||\ + defined ZS_HAVE_OSX || defined ZS_HAVE_QNXNTO ||\ + defined ZS_HAVE_HPUX || defined ZS_HAVE_AIX + +#include +#include +#include + +#include "i_poller.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Implements socket polling mechanism using the POSIX.1-2001 + // poll() system call. + + class poll_t : public i_poller + { + public: + + poll_t (); + virtual ~poll_t () {} + + // i_poller implementation. + handle_t add_fd (fd_t fd_, 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 (i_poll_events *events_); + void cancel_timer (i_poll_events *events_); + int get_load (); + void start (); + void stop (); + void join (); + + 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_table; + + // Pollset to pass to the poll function. + typedef std::vector 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 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/pub.cpp b/src/pub.cpp new file mode 100644 index 0000000..70add18 --- /dev/null +++ b/src/pub.cpp @@ -0,0 +1,38 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "pub.hpp" +#include "app_thread.hpp" +#include "session.hpp" +#include "err.hpp" + +zs::pub_t::pub_t (app_thread_t *thread_, session_t *session_) : + socket_base_t (thread_, session_) +{ + disable_in (); +} + +int zs::pub_t::recv (struct zs_msg *msg_, int flags_) +{ + // Publisher socket has no recv function. + errno = ENOTSUP; + return -1; +} diff --git a/src/pub.hpp b/src/pub.hpp new file mode 100644 index 0000000..b071318 --- /dev/null +++ b/src/pub.hpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_PUB_HPP_INCLUDED__ +#define __ZS_PUB_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zs +{ + + class pub_t : public socket_base_t + { + public: + + pub_t (class app_thread_t *thread_, class session_t *session_); + + // i_api overloads. + int recv (struct zs_msg *msg_, int flags_); + + private: + + pub_t (const pub_t&); + void operator = (const pub_t&); + }; + +} + +#endif diff --git a/src/rep.cpp b/src/rep.cpp new file mode 100644 index 0000000..586c7ed --- /dev/null +++ b/src/rep.cpp @@ -0,0 +1,29 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "rep.hpp" +#include "app_thread.hpp" +#include "session.hpp" + +zs::rep_t::rep_t (app_thread_t *thread_, session_t *session_) : + socket_base_t (thread_, session_) +{ +} diff --git a/src/rep.hpp b/src/rep.hpp new file mode 100644 index 0000000..e11eaa3 --- /dev/null +++ b/src/rep.hpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_REP_HPP_INCLUDED__ +#define __ZS_REP_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zs +{ + + class rep_t : public socket_base_t + { + public: + + rep_t (class app_thread_t *thread_, class session_t *session_); + + private: + + 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..d7a9ad7 --- /dev/null +++ b/src/req.cpp @@ -0,0 +1,29 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "req.hpp" +#include "app_thread.hpp" +#include "session.hpp" + +zs::req_t::req_t (app_thread_t *thread_, session_t *session_) : + socket_base_t (thread_, session_) +{ +} diff --git a/src/req.hpp b/src/req.hpp new file mode 100644 index 0000000..678897d --- /dev/null +++ b/src/req.hpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_REQ_HPP_INCLUDED__ +#define __ZS_REQ_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zs +{ + + class req_t : public socket_base_t + { + public: + + req_t (class app_thread_t *thread_, class session_t *session_); + + private: + + req_t (const req_t&); + void operator = (const req_t&); + }; + +} + +#endif diff --git a/src/safe_object.cpp b/src/safe_object.cpp new file mode 100644 index 0000000..a7c72e6 --- /dev/null +++ b/src/safe_object.cpp @@ -0,0 +1,76 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "safe_object.hpp" + +zs::safe_object_t::safe_object_t (class dispatcher_t *dispatcher_, + int thread_slot_) : + object_t (dispatcher_, thread_slot_), + processed_seqnum (0), + terminating (false) +{ +} + +zs::safe_object_t::safe_object_t (object_t *parent_) : + object_t (parent_), + processed_seqnum (0), + terminating (false) +{ +} + +void zs::safe_object_t::inc_seqnum () +{ + // This function is called from the sender thread to ensure that this + // object will still exist when the command sent to it arrives in the + // destination thread. + sent_seqnum.add (1); +} + +void zs::safe_object_t::process_command (struct command_t &cmd_) +{ + object_t::process_command (cmd_); + + // Adjust sequence number of the last processed command. + processed_seqnum++; + + // If we are already in the termination phase and all commands sent to + // this object are processed, it's safe to deallocate it. + if (terminating && sent_seqnum.get () == processed_seqnum) + delete this; +} + +void zs::safe_object_t::terminate () +{ + // Wait till all commands sent to this session are processed. + terminating = true; + + // If there's no pending command we can deallocate the session + // straight saway. + if (sent_seqnum.get () == processed_seqnum) + delete this; +} + +bool zs::safe_object_t::is_terminating () +{ + return terminating; +} + +zs::safe_object_t::~safe_object_t () +{ +} diff --git a/src/safe_object.hpp b/src/safe_object.hpp new file mode 100644 index 0000000..efa0d2f --- /dev/null +++ b/src/safe_object.hpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SAFE_OBJECT_HPP_INCLUDED__ +#define __ZS_SAFE_OBJECT_HPP_INCLUDED__ + +#include "object.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Same as object_t with the exception of termination mechanism. While + // object_t is destroyed immediately on terminate (assuming that the caller + // have ensured that there are no more commands for the object on the + // fly), safe_object_t switches into termination mode and waits for all + // the on-the-fly commands to be delivered before it deallocates itself. + + class safe_object_t : public object_t + { + public: + + safe_object_t (class dispatcher_t *dispatcher_, int thread_slot_); + safe_object_t (object_t *parent_); + + void inc_seqnum (); + void process_command (struct command_t &cmd_); + + protected: + + void terminate (); + bool is_terminating (); + + virtual ~safe_object_t (); + + private: + + // Sequence number of the last command sent to the object and last + // command processed by the object. The former is an atomic counter + // meaning that other threads can increment it safely. + atomic_counter_t sent_seqnum; + uint32_t processed_seqnum; + + bool terminating; + + safe_object_t (const safe_object_t&); + void operator = (const safe_object_t&); + }; + +} + +#endif diff --git a/src/select.cpp b/src/select.cpp new file mode 100644 index 0000000..9776db3 --- /dev/null +++ b/src/select.cpp @@ -0,0 +1,236 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" + +#include +#include + +#ifdef ZS_HAVE_WINDOWS +#include "winsock2.h" +#elif defined ZS_HAVE_HPUX +#include +#include +#include +#elif defined ZS_HAVE_OPENVMS +#include +#include +#else +#include +#endif + +#include "select.hpp" +#include "err.hpp" +#include "config.hpp" +#include "i_poll_events.hpp" + +zs::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); +} + +zs::handle_t zs::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); + + handle_t handle; + handle.fd = fd_; + return handle; +} + +void zs::select_t::rm_fd (handle_t handle_) +{ + // Get file descriptor. + fd_t fd = handle_.fd; + + // Mark the descriptor as retired. + fd_set_t::iterator it; + for (it = fds.begin (); it != fds.end (); it ++) + if (it->fd == fd) + break; + zs_assert (it != fds.end ()); + it->fd = retired_fd; + retired = true; + + // Stop polling on the descriptor. + FD_CLR (fd, &source_set_in); + FD_CLR (fd, &source_set_out); + FD_CLR (fd, &source_set_err); + + // Discard all events generated on this file descriptor. + FD_CLR (fd, &readfds); + FD_CLR (fd, &writefds); + FD_CLR (fd, &exceptfds); + + // Adjust the maxfd attribute if we have removed the + // highest-numbered file descriptor. + if (fd == 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 zs::select_t::set_pollin (handle_t handle_) +{ + FD_SET (handle_.fd, &source_set_in); +} + +void zs::select_t::reset_pollin (handle_t handle_) +{ + FD_CLR (handle_.fd, &source_set_in); +} + +void zs::select_t::set_pollout (handle_t handle_) +{ + FD_SET (handle_.fd, &source_set_out); +} + +void zs::select_t::reset_pollout (handle_t handle_) +{ + FD_CLR (handle_.fd, &source_set_out); +} + +void zs::select_t::add_timer (i_poll_events *events_) +{ + timers.push_back (events_); +} + +void zs::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 zs::select_t::get_load () +{ + return load.get (); +} + +void zs::select_t::start () +{ + worker.start (worker_routine, this); +} + +void zs::select_t::stop () +{ + stopping = true; +} + +void zs::select_t::join () +{ + worker.stop (); +} + +void zs::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 ZS_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 zs::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..7151f84 --- /dev/null +++ b/src/select.hpp @@ -0,0 +1,122 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SELECT_HPP_INCLUDED__ +#define __ZS_SELECT_HPP_INCLUDED__ + +#include "platform.hpp" + +#include +#include + +#ifdef ZS_HAVE_WINDOWS +#include "winsock2.h" +#elif defined ZS_HAVE_OPENVMS +#include +#include +#else +#include +#endif + +#include "i_poller.hpp" +#include "fd.hpp" +#include "thread.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Implements socket polling mechanism using POSIX.1-2001 select() + // function. + + class select_t : public i_poller + { + public: + + select_t (); + + // i_poller implementation. + handle_t add_fd (fd_t fd_, 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 (i_poll_events *events_); + void cancel_timer (i_poll_events *events_); + int get_load (); + void start (); + void stop (); + void join (); + + 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_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 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..63868b2 --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,273 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "session.hpp" +#include "i_engine.hpp" +#include "i_thread.hpp" +#include "i_mux.hpp" +#include "i_demux.hpp" +#include "err.hpp" +#include "pipe.hpp" +#include "pipe_reader.hpp" +#include "pipe_writer.hpp" +#include "simple_semaphore.hpp" + +zs::session_t::session_t (object_t *parent_, i_thread *thread_, + i_mux *mux_, i_demux *demux_, + bool terminate_on_disconnect_, bool terminate_on_no_pipes_) : + safe_object_t (parent_), + mux (mux_), + demux (demux_), + thread (thread_), + engine (NULL), + terminate_on_disconnect (terminate_on_disconnect_), + terminate_on_no_pipes (false), + terminate_on_no_pipes_delayed (terminate_on_no_pipes_), + index (-1) +{ + // At least one way to terminate the session should be allowed. Otherwise + // the session can be orphaned forever. + zs_assert (terminate_on_disconnect || terminate_on_no_pipes_delayed); + + // Give the mux and the demux callback pointer to ourselves. + if (mux) + mux->set_session (this); + if (demux) + demux->set_session (this); +} + +void zs::session_t::shutdown () +{ + // Session may live even without an associated engine, thus we have + // to check if for NULL value. + if (engine) + engine->shutdown (); + + // Propagate the shutdown signal to both inbound and outbound pipes. + if (mux) + mux->shutdown (); + if (demux) + demux->shutdown (); + + delete this; +} + +void zs::session_t::disconnected () +{ + // It's engine who calls this function so there's no need to deallocate + // the engine. Just drop the reference. + engine = NULL; + + // Some sessions won't shut down because of disconnect. New engine will + // attached to the session later on. + if (!terminate_on_disconnect) + return; + + terminate (); +} + +void zs::session_t::bind (object_t *peer_, bool in_, bool out_) +{ + // Create the out pipe (if required). + pipe_reader_t *pipe_reader = NULL; + if (out_) { + pipe_writer_t *pipe_writer; + create_pipe (peer_, this, 0, 0, &pipe_reader, &pipe_writer); + demux->attach_pipe (pipe_writer); + + // There's at least one pipe attached. We can deallocate the object + // when there are no pipes (if required). + terminate_on_no_pipes = terminate_on_no_pipes_delayed; + } + + // Ask peer to attach to the out pipe (if one exists). If required, ask + // it to create a pipe in opposite direction. It's assumed that peer's + // seqnum was already incremented, so we don't need to care whether it's + // alive at the moment. + if (in_) + inc_seqnum (); + send_bind (peer_, pipe_reader, in_ ? this : NULL); +} + +void zs::session_t::revive () +{ + if (engine) + engine->revive (); +} + +void zs::session_t::terminate () +{ + // Terminate is always called by engine, thus it'll terminate itself, + // we just have to drop the pointer. + engine = NULL; + + // Propagate the terminate signal to both inbound and outbound pipes. + if (mux) { + mux->terminate (); + mux = NULL; + } + if (demux) { + demux->terminate (); + demux = NULL; + } + + // Session cannot be deallocated at this point. There can still be + // pending commands to process. Unregister session from global + // repository thus ensuring that no new commands will be sent. + unregister_inproc_endpoints (this); + + // Move to terminating state. + safe_object_t::terminate (); +} + +zs::session_t::~session_t () +{ + // When session is actually deallocated it unregisters from its thread. + // Unregistration cannot be done earlier as it would result in memory + // leak if global shutdown happens in the middle of session termination. + thread->detach_session (this); +} + +void zs::session_t::set_engine (i_engine *engine_) +{ + zs_assert (!engine || !engine_); + engine = engine_; +} + +void zs::session_t::set_index (int index_) +{ + index = index_; +} + +int zs::session_t::get_index () +{ + return index; +} + +bool zs::session_t::write (zs_msg *msg_) +{ + return demux->send (msg_); +} + +void zs::session_t::flush () +{ + demux->flush (); +} + +bool zs::session_t::read (zs_msg *msg_) +{ + bool retrieved = mux->recv (msg_); + if (terminate_on_no_pipes && mux->empty () && demux->empty ()) { + zs_assert (engine); + engine->schedule_terminate (); + terminate (); + } + return retrieved; +} + +void zs::session_t::process_bind (pipe_reader_t *reader_, session_t *peer_) +{ + if (is_terminating ()) { + + // If session is already in termination phase, we'll ask newly arrived + // pipe reader & writer to terminate straight away. + if (reader_) + reader_->terminate (); + + // Peer session has already incremented its seqnum. We have to send + // a dummy command to avoid a memory leak. + if (peer_) + send_bind (peer_, NULL, NULL); + + return; + } + + // If inbound pipe is provided, bind it to the mux. + if (reader_) { + mux->attach_pipe (reader_); + + // There's at least one pipe attached. We can deallocate the object + // when there are no pipes (if required). + terminate_on_no_pipes = terminate_on_no_pipes_delayed; + } + + // If peer wants to get messages from ourselves, we'll bind to it. + if (peer_) { + pipe_reader_t *pipe_reader; + pipe_writer_t *pipe_writer; + create_pipe (peer_, this, 0, 0, &pipe_reader, &pipe_writer); + demux->attach_pipe (pipe_writer); + send_bind (peer_, pipe_reader, NULL); + + // There's at least one pipe attached. We can deallocate the object + // when there are no pipes (if required). + terminate_on_no_pipes = terminate_on_no_pipes_delayed; + } +} + +void zs::session_t::process_reg (simple_semaphore_t *smph_) +{ + zs_assert (!is_terminating ()); + + // Add the session to the list of sessions associated with this I/O thread. + // This way the session will be deallocated on the terminal shutdown. + thread->attach_session (this); + + // Release calling thead (if required). + if (smph_) + smph_->post (); +} + +void zs::session_t::process_reg_and_bind (session_t *peer_, + bool flow_in_, bool flow_out_) +{ + zs_assert (!is_terminating ()); + + // Add the session to the list of sessions associated with this I/O thread. + // This way the session will be deallocated on the terminal shutdown. + thread->attach_session (this); + + // Bind to the peer. Note that caller have already incremented command + // sequence number of the peer so we are sure it still exists. + pipe_reader_t *pipe_reader = NULL; + if (flow_out_) { + pipe_writer_t *pipe_writer; + create_pipe (peer_, this, 0, 0, &pipe_reader, &pipe_writer); + demux->attach_pipe (pipe_writer); + + // There's at least one pipe attached. We can deallocate the object + // when there are no pipes (if required). + terminate_on_no_pipes = terminate_on_no_pipes_delayed; + } + send_bind (peer_, pipe_reader, flow_in_ ? this : NULL); +} + +void zs::session_t::process_engine (i_engine *engine_) +{ + if (is_terminating ()) { + + // Kill the engine. It won't be needed anymore. + engine_->terminate (); + return; + } + + engine_->attach (thread->get_poller (), this); +} diff --git a/src/session.hpp b/src/session.hpp new file mode 100644 index 0000000..3cdace2 --- /dev/null +++ b/src/session.hpp @@ -0,0 +1,107 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SESSION_HPP_INCLUDED__ +#define __ZS_SESSION_HPP_INCLUDED__ + +#include "i_session.hpp" +#include "safe_object.hpp" +#include "stdint.hpp" +#include "atomic_counter.hpp" + +namespace zs +{ + + // Object that encapsulates both mux and demux. + + class session_t : public safe_object_t, public i_session + { + public: + + // Creates the session object. + session_t (struct object_t *parent_, struct i_thread *thread_, + struct i_mux *mux_, struct i_demux *demux_, + bool terminate_on_disconnect_, bool terminate_on_no_pipes_); + + // i_session implementation + void set_engine (struct i_engine *engine_); + void shutdown (); + bool read (struct zs_msg *msg_); + bool write (struct zs_msg *msg_); + void flush (); + + // Called by the engine when it is being closed. + void disconnected (); + + // Creates a message flow between this session and the peer session. + // If in_ is true, the messages can flow from the peer to ourselves. + // If out_ is true, messages can flow from ourselves to the peer. + // It's assumed that peer's seqnum was already incremented. + void bind (class object_t *peer_, bool in_, bool out_); + + // Called by mux if new messages are available. + void revive (); + + // Functions to set & retrieve index of this MD in thread's array + // of session objects. + void set_index (int index_); + int get_index (); + + private: + + // Clean-up. + ~session_t (); + + // Terminate is private here. It is called by either when disconnected + // or no_pipes event occurs. + void terminate (); + + void process_bind (class pipe_reader_t *reader_, + class session_t *peer_); + void process_reg (class simple_semaphore_t *smph_); + void process_reg_and_bind (class session_t *peer_, + bool flow_in_, bool flow_out_); + void process_engine (i_engine *engine_); + + struct i_mux *mux; + struct i_demux *demux; + + struct i_thread *thread; + struct i_engine *engine; + + // If true termination of the session can be triggered by engine + // disconnect/close. + bool terminate_on_disconnect; + + // If true termination of the session can be triggered when the last + // pipe detaches from it. + bool terminate_on_no_pipes; + + // If true, terminate_on_no_pipes should be set when at least one + // pipe was bound. + bool terminate_on_no_pipes_delayed; + + // Index in thread's session array. + int index; + }; + +} + +#endif + diff --git a/src/session_stub.cpp b/src/session_stub.cpp new file mode 100644 index 0000000..3bebcb0 --- /dev/null +++ b/src/session_stub.cpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include + +#include "../include/zs.h" + +#include "session_stub.hpp" +#include "i_engine.hpp" +#include "listener.hpp" +#include "err.hpp" + +zs::session_stub_t::session_stub_t (listener_t *listener_) : + state (reading_identity), + engine (NULL), + listener (listener_), + index (-1) +{ +} + +void zs::session_stub_t::terminate () +{ + if (engine) + engine->terminate (); + delete this; +} + +void zs::session_stub_t::shutdown () +{ + if (engine) + engine->shutdown (); + delete this; +} + +zs::session_stub_t::~session_stub_t () +{ +} + +void zs::session_stub_t::set_engine (i_engine *engine_) +{ + zs_assert (!engine_ || !engine); + engine = engine_; +} + +bool zs::session_stub_t::read (struct zs_msg *msg_) +{ + // No messages are sent to the connecting peer. + return false; +} + +bool zs::session_stub_t::write (struct zs_msg *msg_) +{ + // The first message arrived is the connection identity. + if (state == reading_identity) { + identity = std::string ((const char*) zs_msg_data (msg_), + zs_msg_size (msg_)); + state = has_identity; + return true; + } + + // We are not interested in any subsequent messages. + return false; +} + +void zs::session_stub_t::flush () +{ + // We have the identity. At this point we can find the correct session and + // attach it to the connection. + if (state == has_identity) { + + // At this point the stub will be deleted. Return immediately without + // touching 'this' pointer. + listener->got_identity (this, identity.c_str ()); + return; + } +} + +zs::i_engine *zs::session_stub_t::detach_engine () +{ + // Ask engine to unregister from the poller. + i_engine *e = engine; + engine->detach (); + return e; +} + +void zs::session_stub_t::set_index (int index_) +{ + index = index_; +} + +int zs::session_stub_t::get_index () +{ + return index; +} diff --git a/src/session_stub.hpp b/src/session_stub.hpp new file mode 100644 index 0000000..2e882f8 --- /dev/null +++ b/src/session_stub.hpp @@ -0,0 +1,83 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SESSION_STUB_HPP_INCLUDED__ +#define __ZS_SESSION_STUB_HPP_INCLUDED__ + +#include + +#include "i_session.hpp" + +namespace zs +{ + + // This class is used instead of regular session till the identity of + // incomming connection is established and connection is attached + // to corresponding session. + + class session_stub_t : public i_session + { + public: + + session_stub_t (class listener_t *listener_); + + // i_session implementation. + void set_engine (struct i_engine *engine_); + void terminate (); + void shutdown (); + bool read (struct zs_msg *msg_); + bool write (struct zs_msg *msg_); + void flush (); + + // Detaches engine from the stub. Returns it to the caller. + struct i_engine *detach_engine (); + + // Manipulate stubs's index in listener's array of stubs. + void set_index (int index_); + int get_index (); + + private: + + // Clean-up. + virtual ~session_stub_t (); + + enum { + reading_identity, + has_identity + } state; + + // Reference to the associated engine. + i_engine *engine; + + // Reference to the listener object that owns this stub. + class listener_t *listener; + + // Index of the stub in listener's array of stubs. + int index; + + // Identity of the connection. + std::string identity; + + session_stub_t (const session_stub_t&); + void operator = (const session_stub_t&); + }; + +} + +#endif diff --git a/src/simple_semaphore.hpp b/src/simple_semaphore.hpp new file mode 100644 index 0000000..1bd114f --- /dev/null +++ b/src/simple_semaphore.hpp @@ -0,0 +1,188 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SIMPLE_SEMAPHORE_HPP_INCLUDED__ +#define __ZS_SIMPLE_SEMAPHORE_HPP_INCLUDED__ + +#include "platform.hpp" +#include "err.hpp" + +#if defined ZS_HAVE_LINUX || defined ZS_HAVE_OSX || defined ZS_HAVE_OPENVMS +#include +#elif defined ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#endif + +namespace zs +{ + + // Simple semaphore. Only single thread may be waiting at any given time. + // Also, the semaphore may not be posted before the previous post + // was matched by corresponding wait and the waiting thread was + // released. + +#if defined ZS_HAVE_LINUX || defined ZS_HAVE_OSX || defined ZS_HAVE_OPENVMS + + // On platforms that allow for double locking of a mutex from the same + // thread, simple semaphore is implemented using mutex, as it is more + // efficient than full-blown semaphore. + + class simple_semaphore_t + { + public: + + // Initialise the semaphore. + inline simple_semaphore_t () + { + int rc = pthread_mutex_init (&mutex, NULL); + errno_assert (rc == 0); + rc = pthread_mutex_lock (&mutex); + errno_assert (rc == 0); + } + + // Destroy the semaphore. + inline ~simple_semaphore_t () + { + int rc = pthread_mutex_unlock (&mutex); + errno_assert (rc == 0); + rc = pthread_mutex_destroy (&mutex); + errno_assert (rc == 0); + } + + // Wait for the semaphore. + inline void wait () + { + int rc = pthread_mutex_lock (&mutex); + errno_assert (rc == 0); + } + + // Post the semaphore. + inline void post () + { + int rc = pthread_mutex_unlock (&mutex); + errno_assert (rc == 0); + } + + private: + + pthread_mutex_t mutex; + + // Disable copying of the object. + simple_semaphore_t (const simple_semaphore_t&); + void operator = (const simple_semaphore_t&); + }; + +#elif defined ZMQ_HAVE_WINDOWS + + // On Windows platform simple semaphore is implemeted using event object. + + class simple_semaphore_t + { + public: + + // Initialise the semaphore. + inline simple_semaphore_t () + { + ev = CreateEvent (NULL, FALSE, FALSE, NULL); + win_assert (ev != NULL); + } + + // Destroy the semaphore. + inline ~simple_semaphore_t () + { + int rc = CloseHandle (ev); + win_assert (rc != 0); + } + + // Wait for the semaphore. + inline void wait () + { + DWORD rc = WaitForSingleObject (ev, INFINITE); + win_assert (rc != WAIT_FAILED); + } + + // Post the semaphore. + inline void post () + { + int rc = SetEvent (ev); + win_assert (rc != 0); + } + + private: + + HANDLE ev; + + // Disable copying of the object. + simple_semaphore_t (const simple_semaphore_t&); + void operator = (const simple_semaphore_t&); + }; + +#else + + // Default implementation maps simple semaphore to standard semaphore. + + class simple_semaphore_t + { + public: + + // Initialise the semaphore. + inline simple_semaphore_t () + { + int rc = sem_init (&sem, 0, 0); + errno_assert (rc != -1); + } + + // Destroy the semaphore. + inline ~simple_semaphore_t () + { + int rc = sem_destroy (&sem); + errno_assert (rc != -1); + } + + // Wait for the semaphore. + inline void wait () + { + int rc = sem_wait (&sem); + errno_assert (rc != -1); + } + + // Post the semaphore. + inline void post () + { + int rc = sem_post (&sem); + errno_assert (rc != -1); + } + + private: + + // Underlying system semaphore object. + sem_t sem; + + // Disable copying of the object. + simple_semaphore_t (const simple_semaphore_t&); + void operator = (const simple_semaphore_t&); + }; + +#endif + +} + +#endif diff --git a/src/socket_base.cpp b/src/socket_base.cpp new file mode 100644 index 0000000..07606ad --- /dev/null +++ b/src/socket_base.cpp @@ -0,0 +1,267 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include + +#include "../include/zs.h" + +#include "socket_base.hpp" +#include "app_thread.hpp" +#include "err.hpp" +#include "listener.hpp" +#include "connecter.hpp" +#include "simple_semaphore.hpp" +#include "io_thread.hpp" +#include "io_object.hpp" +#include "session.hpp" +#include "dummy_aggregator.hpp" +#include "dummy_distributor.hpp" + +zs::socket_base_t::socket_base_t (app_thread_t *thread_, session_t *session_) : + object_t (thread_), + thread (thread_), + session (session_), + has_in (true), + has_out (true) +{ + session->set_engine (this); +} + +void zs::socket_base_t::shutdown () +{ + // Destroy all the I/O objects created from this socket. + for (io_objects_t::size_type i = 0; i != io_objects.size (); i++) + io_objects [i]->shutdown (); + + delete this; +} + +void zs::socket_base_t::schedule_terminate () +{ + // Terminate is never scheduled on socket engines. + zs_assert (false); +} + +void zs::socket_base_t::terminate () +{ + // Destroy all the I/O objects created from this socket. + // First unregister the object from I/O thread, then terminate it in + // this application thread. + simple_semaphore_t smph; + for (io_objects_t::size_type i = 0; i != io_objects.size (); i++) { + send_unreg (io_objects [i], &smph); + smph.wait (); + io_objects [i]->terminate (); + } + + zs_assert (session); + session->disconnected (); + + delete this; +} + +zs::socket_base_t::~socket_base_t () +{ +} + +void zs::socket_base_t::disable_in () +{ + has_in = false; +} + +void zs::socket_base_t::disable_out () +{ + has_out = false; +} + +int zs::socket_base_t::bind (const char *addr_, zs_opts *opts_) +{ + thread->process_commands (false); + + std::string addr (addr_); + std::string::size_type pos = addr.find ("://"); + if (pos == std::string::npos || addr.substr (0, pos) == "zmq.tcp") { + + // Choose the I/O thread with the least load, create the listener. + // Note that same taskset is used to choose the I/O thread to handle + // the listening socket and newly created connections. + // Note that has_in and has_out are twisted at this place - listener + // is going to create peer objects, so the message flows are viewed + // from the opposite direction. + io_thread_t *io_thread = choose_io_thread (opts_ ? opts_->taskset : 0); + listener_t *listener = new listener_t (io_thread, addr_, session, + has_out, has_in, opts_ ? opts_->taskset : 0); + + // Ask it to start interacting with the I/O thread. + simple_semaphore_t smph; + send_reg (listener, &smph); + + // Store the reference to the listener so that it can be terminated + // when the socket is closed. + io_objects.push_back (listener); + + // Wait while listener is actually registered with the I/O thread. + smph.wait (); + + return 0; + } + else if (addr.substr (0, pos) == "inproc") { + + // For inproc transport the only thing we have to do is to register + // this socket as an inproc endpoint with the supplied name. + return register_inproc_endpoint (addr.substr (pos + 3).c_str (), + session); + } + else { + + // Unknown protocol requested. + errno = EINVAL; + return -1; + } +} + +int zs::socket_base_t::connect (const char *addr_, zs_opts *opts_) +{ + thread->process_commands (false); + + std::string addr (addr_); + std::string::size_type pos = addr.find ("://"); + if (pos == std::string::npos || addr.substr (0, pos) == "zmq.tcp") { + + // Choose the I/O thread with the least load, create the connecter and + // session. + io_thread_t *io_thread = choose_io_thread (opts_ ? opts_->taskset : 0); + i_mux *mux = new dummy_aggregator_t; + zs_assert (mux); + i_demux *demux = new dummy_distributor_t; + zs_assert (demux); + session_t *peer = new session_t (io_thread, io_thread, mux, demux, + false, true); + zs_assert (peer); + connecter_t *connecter = new connecter_t (io_thread, addr_, peer); + zs_assert (connecter); + + // Increment session's command sequence number so that it won't get + // deallocated till the subsequent bind command arrives. + peer->inc_seqnum (); + + // Register the connecter (and session) with its I/O thread. + simple_semaphore_t smph; + send_reg (connecter, &smph); + + // Store the reference to the connecter so that it can be terminated + // when the socket is closed. + io_objects.push_back (connecter); + + // Wait till registration succeeds. + smph.wait (); + + // Bind local session with the connecter's session so that messages + // can flow in both directions. + session->bind (peer, has_in, has_out); + + return 0; + } + else if (addr.substr (0, pos) == "inproc") { + + // Get the MD responsible for the address. In case of invalid address + // return error. + object_t *peer = get_inproc_endpoint (addr.substr (pos + 3).c_str ()); + if (!peer) { + errno = EADDRNOTAVAIL; + return -1; + } + + // Create bidirectional message pipes between this session and + // the peer session. + session->bind (peer, has_in, has_out); + + return 0; + } + else { + + // Unknown protocol requested. + errno = EINVAL; + return -1; + } +} + +int zs::socket_base_t::subscribe (const char *criteria_) +{ + // No implementation at the moment... + errno = ENOTSUP; + return -1; +} + +int zs::socket_base_t::send (zs_msg *msg_, int flags_) +{ + thread->process_commands (false); + while (true) { + if (session->write (msg_)) + return 0; + if (flags_ & ZS_NOBLOCK) { + errno = EAGAIN; + return -1; + } + thread->process_commands (true); + } +} + +int zs::socket_base_t::flush () +{ + thread->process_commands (false); + session->flush (); + return 0; +} + +int zs::socket_base_t::recv (zs_msg *msg_, int flags_) +{ + thread->process_commands (false); + while (true) { + if (session->read (msg_)) + return 0; + if (flags_ & ZS_NOBLOCK) { + errno = EAGAIN; + return -1; + } + thread->process_commands (true); + } +} + +int zs::socket_base_t::close () +{ + terminate (); + return 0; +} + +void zs::socket_base_t::attach (struct i_poller *poller_, i_session *session_) +{ + zs_assert (false); +} + +void zs::socket_base_t::detach () +{ + zs_assert (false); +} + +void zs::socket_base_t::revive () +{ + // We can ignore the event safely here. +} + diff --git a/src/socket_base.hpp b/src/socket_base.hpp new file mode 100644 index 0000000..ed0272a --- /dev/null +++ b/src/socket_base.hpp @@ -0,0 +1,96 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SOCKET_BASE_HPP_INCLUDED__ +#define __ZS_SOCKET_BASE_HPP_INCLUDED__ + +#include + +#include "i_engine.hpp" +#include "i_api.hpp" +#include "object.hpp" + +namespace zs +{ + + class socket_base_t : public object_t, public i_engine, public i_api + { + public: + + // TODO: Possibly, session can be attached to the engine using + // attach function. + socket_base_t (class app_thread_t *thread_, class session_t *session_); + + // i_engine interface implementation. + void attach (struct i_poller *poller_, struct i_session *session_); + void detach (); + void revive (); + void schedule_terminate (); + void terminate (); + void shutdown (); + + // i_api interface implementation. + int bind (const char *addr_, struct zs_opts *opts_); + int connect (const char *addr_, struct zs_opts *opts_); + int subscribe (const char *criteria_); + int send (struct zs_msg *msg_, int flags_); + int flush (); + int recv (struct zs_msg *msg_, int flags_); + int close (); + + protected: + + // Clean-up. The function has to be protected rather than private, + // otherwise auto-generated destructor in derived classes + // cannot be compiled. It has to be virtual so that socket_base_t's + // terminate & shutdown functions deallocate correct type of object. + virtual ~socket_base_t (); + + // By default, socket is able to pass messages in both inward and + // outward directions. By calling these functions, particular + // socket type is able to eliminate one direction. + void disable_in (); + void disable_out (); + + private: + + // Pointer to the application thread the socket belongs to. + class app_thread_t *thread; + + // Pointer to the associated session object. + class session_t *session; + + // List of I/O object created via this socket. These have to be shut + // down when the socket is closed. + typedef std::vector io_objects_t; + io_objects_t io_objects; + + // If true, socket creates inbound pipe when binding to an engine. + bool has_in; + + // If true, socket creates outbound pipe when binding to an engine. + bool has_out; + + 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..b4c6125 --- /dev/null +++ b/src/stdint.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_STDINT_HPP_INCLUDED__ +#define __ZS_STDINT_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZS_HAVE_SOLARIS + +#include + +#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 +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#else + +#include + +#endif + +#endif diff --git a/src/sub.cpp b/src/sub.cpp new file mode 100644 index 0000000..59e838a --- /dev/null +++ b/src/sub.cpp @@ -0,0 +1,45 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include "sub.hpp" +#include "app_thread.hpp" +#include "session.hpp" +#include "err.hpp" + +zs::sub_t::sub_t (app_thread_t *thread_, session_t *session_) : + socket_base_t (thread_, session_) +{ + disable_out (); +} + +int zs::sub_t::send (struct zs_msg *msg_, int flags_) +{ + // Subscriber socket has no send function. + errno = ENOTSUP; + return -1; +} + +int zs::sub_t::flush () +{ + // Subscriber socket has no flush function. + errno = ENOTSUP; + return -1; +} diff --git a/src/sub.hpp b/src/sub.hpp new file mode 100644 index 0000000..b7c2bd2 --- /dev/null +++ b/src/sub.hpp @@ -0,0 +1,46 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_SUB_HPP_INCLUDED__ +#define __ZS_SUB_HPP_INCLUDED__ + +#include "socket_base.hpp" + +namespace zs +{ + + class sub_t : public socket_base_t + { + public: + + sub_t (class app_thread_t *thread_, class session_t *session_); + + // i_api overloads. + int send (struct zs_msg *msg_, int flags_); + int flush (); + + private: + + 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..6b59290 --- /dev/null +++ b/src/tcp_connecter.cpp @@ -0,0 +1,138 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "tcp_connecter.hpp" +#include "platform.hpp" +#include "ip.hpp" +#include "err.hpp" + +#ifdef ZS_HAVE_WINDOWS + +#include "windows.hpp" +#error + +#else + +#include +#include +#include +#include +#include +#include +#include + +zs::tcp_connecter_t::tcp_connecter_t () : + s (retired_fd) +{ +} + +zs::tcp_connecter_t::~tcp_connecter_t () +{ + if (s != retired_fd) + close (); +} + +int zs::tcp_connecter_t::open (const char *addr_) +{ + zs_assert (s == retired_fd); + + // Convert the hostname into sockaddr_in structure. + sockaddr_in address; + int rc = resolve_ip_hostname (&address, addr_); + if (rc != 0) + return -1; + + // Create the socket. + s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s == -1) + return -1; + + // Set to non-blocking mode. + int flags = fcntl (s, F_GETFL, 0); + if (flags == -1) + flags = 0; + rc = fcntl (s, F_SETFL, flags | O_NONBLOCK); + errno_assert (rc != -1); + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (s, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, sizeof (int)); + errno_assert (rc == 0); + +#ifdef ZS_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, (sockaddr*) &address, sizeof address); + + // Connect was successfull immediately. + if (rc == 0) + return 0; + + // Asynchronous connect was launched. + if (rc == -1 && errno == EINPROGRESS) + return 1; + + // Error occured. + int err = errno; + close (); + errno = err; + return -1; +} + +int zs::tcp_connecter_t::close () +{ + zs_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + return 0; +} + +zs::fd_t zs::tcp_connecter_t::get_fd () +{ + return s; +} + +zs::fd_t zs::tcp_connecter_t::connect () +{ + // Following code should handle both Berkeley-derived socket + // implementations and Solaris. + int err = 0; + socklen_t len = sizeof err; + int rc = getsockopt (s, SOL_SOCKET, SO_ERROR, (char*) &err, &len); + if (rc == -1) + err = errno; + if (err != 0) { + close (); + 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..ef11242 --- /dev/null +++ b/src/tcp_connecter.hpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_TCP_CONNECTER_HPP_INCLUDED__ +#define __ZS_TCP_CONNECTER_HPP_INCLUDED__ + +#include "fd.hpp" + +namespace zs +{ + + // The class encapsulating simple TCP listening socket. + + class tcp_connecter_t + { + public: + + tcp_connecter_t (); + ~tcp_connecter_t (); + + // Open TCP connecting socket. Address is in + // : format. Returns -1 in case of error, + // 0 if connect was successfull immediately and 1 if async connect + // was launched. + int open (const char *addr_); + + // 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: + + // 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..3703950 --- /dev/null +++ b/src/tcp_listener.cpp @@ -0,0 +1,165 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "tcp_listener.hpp" +#include "platform.hpp" +#include "ip.hpp" +#include "config.hpp" +#include "err.hpp" + +#ifdef ZS_HAVE_WINDOWS + +#include "windows.hpp" +#error + +#else + +#include +#include +#include +#include +#include +#include +#include + +zs::tcp_listener_t::tcp_listener_t () : + s (retired_fd) +{ +} + +zs::tcp_listener_t::~tcp_listener_t () +{ + if (s != retired_fd) + close (); +} + +int zs::tcp_listener_t::open (const char *addr_) +{ + // Convert the interface into sockaddr_in structure. + sockaddr_in ip_address; + int rc = resolve_ip_interface (&ip_address, addr_); + if (rc != 0) + return -1; + + // Create a listening socket. + s = socket (AF_INET, 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. + 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 network interface and port. + rc = bind (s, (struct sockaddr*) &ip_address, sizeof (ip_address)); + if (rc != 0) { + close (); + return -1; + } + + // Listen for incomming connections. + rc = listen (s, tcp_connection_backlog); + if (rc != 0) { + close (); + return -1; + } + + return 0; +} + +int zs::tcp_listener_t::close () +{ + zs_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + return 0; +} + +zs::fd_t zs::tcp_listener_t::get_fd () +{ + return s; +} + +zs::fd_t zs::tcp_listener_t::accept () +{ + zs_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) + 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. + 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); + + // Disable Nagle's algorithm. + int flag = 1; + rc = setsockopt (sock, IPPROTO_TCP, TCP_NODELAY, (char*) &flag, + sizeof (int)); + errno_assert (rc == 0); + +#ifdef ZS_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..156195b --- /dev/null +++ b/src/tcp_listener.hpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_TCP_LISTENER_HPP_INCLUDED__ +#define __ZS_TCP_LISTENER_HPP_INCLUDED__ + +#include "fd.hpp" + +namespace zs +{ + + // The class encapsulating simple TCP listening socket. + + class tcp_listener_t + { + public: + + tcp_listener_t (); + ~tcp_listener_t (); + + // Open TCP listining socket. Address is in + // : format. Interface name may be '*' + // to bind to all the interfaces. + int open (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: + + // 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..70fde96 --- /dev/null +++ b/src/tcp_socket.cpp @@ -0,0 +1,116 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "tcp_socket.hpp" +#include "platform.hpp" +#include "err.hpp" + +#ifdef ZS_HAVE_WINDOWS + +#include "windows.hpp" +#error + +#else + +#include +#include +#include +#include +#include +#include +#include + +zs::tcp_socket_t::tcp_socket_t () : + s (retired_fd) +{ +} + +zs::tcp_socket_t::~tcp_socket_t () +{ + if (s != retired_fd) + close (); +} + +int zs::tcp_socket_t::open (fd_t fd_) +{ + assert (s == retired_fd); + s = fd_; + return 0; +} + +int zs::tcp_socket_t::close () +{ + zs_assert (s != retired_fd); + int rc = ::close (s); + if (rc != 0) + return -1; + s = retired_fd; + return 0; +} + +zs::fd_t zs::tcp_socket_t::get_fd () +{ + return s; +} + +int zs::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) + return -1; + + errno_assert (nbytes != -1); + return (size_t) nbytes; +} + +int zs::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..a6c61ac --- /dev/null +++ b/src/tcp_socket.hpp @@ -0,0 +1,70 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_TCP_SOCKET_HPP_INCLUDED__ +#define __ZS_TCP_SOCKET_HPP_INCLUDED__ + +#include "fd.hpp" + +namespace zs +{ + + // 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_); + + // 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..7cf54f2 --- /dev/null +++ b/src/thread.cpp @@ -0,0 +1,88 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "thread.hpp" +#include "err.hpp" +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS + +void zs::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 zs::thread_t::stop () +{ + DWORD rc = WaitForSingleObject (descriptor, INFINITE); + win_assert (rc != WAIT_FAILED); +} + +unsigned int __stdcall zs::thread_t::thread_routine (void *arg_) +{ + thread_t *self = (thread_t*) arg_; + self->tfn (self->arg); + return 0; +} + +#else + +#include + +void zs::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 zs::thread_t::stop () +{ + int rc = pthread_join (descriptor, NULL); + errno_assert (rc == 0); +} + +void *zs::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..6ee9194 --- /dev/null +++ b/src/thread.hpp @@ -0,0 +1,77 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_THREAD_HPP_INCLUDED__ +#define __ZS_THREAD_HPP_INCLUDED__ + +#include "platform.hpp" + +#ifdef ZS_HAVE_WINDOWS +#include "windows.hpp" +#else +#include +#endif + +namespace zs +{ + + 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 (); + + private: + +#ifdef ZS_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/uuid.cpp b/src/uuid.cpp new file mode 100644 index 0000000..d9883cd --- /dev/null +++ b/src/uuid.cpp @@ -0,0 +1,136 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "platform.hpp" +#include "uuid.hpp" +#include "err.hpp" + +#if defined ZS_HAVE_WINDOWS + +#include + +zs::uuid_t::uuid_t () +{ + RPC_STATUS ret = UuidCreate (&uuid); + zs_assert (ret == RPC_S_OK); + ret = UuidToString (&uuid, &uuid_str); + zs_assert (ret == RPC_S_OK); +} + +zs::uuid_t::~uuid_t () +{ + RPC_STATUS ret = RpcStringFree(&uuid_str); + assert (ret == RPC_S_OK); +} + +const char *zs::uuid_t::to_string () +{ + return uuid_str; +} + +#elif defined ZS_HAVE_FREEBSD + +#include +#include + +zs::uuid_t::uuid_t () +{ + uint32_t status; + uuid_create (&uuid, &status); + zs_assert (status == uuid_s_ok); + uuid_to_string (&uuid, &uuid_str, &status); + zs_assert (status == uuid_s_ok); +} + +zs::uuid_t::~uuid_t () +{ + free (uuid_str); +} + +const char *zs::uuid_t::to_string () +{ + return uuid_str; +} + +#elif defined ZS_HAVE_LINUX || defined ZS_HAVE_SOLARIS || defined ZS_HAVE_OSX + +#include + +zs::uuid_t::uuid_t () +{ + uuid_generate (uuid); + uuid_unparse (uuid, uuid_buf); +} + +zs::uuid_t::~uuid_t () +{ +} + +const char *zs::uuid_t::to_string () +{ + return uuid_buf; +} + +#else + +#include +#include +#include + +zs::uuid_t::uuid_t () +{ + unsigned char rand_buf [16]; + int ret = RAND_bytes (rand_buf, sizeof rand_buf); + zs_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 (uuid_buf, sizeof uuid_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]); +} + +zs::uuid_t::~uuid_t () +{ +} + +const char *zs::uuid_t::to_string () +{ + return uuid_buf; +} + +#endif diff --git a/src/uuid.hpp b/src/uuid.hpp new file mode 100644 index 0000000..94c3e8a --- /dev/null +++ b/src/uuid.hpp @@ -0,0 +1,82 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_UUID_HPP_INCLUDED__ +#define __ZS_UUID_HPP_INCLUDED__ + +#include "platform.hpp" + +#if defined ZS_HAVE_WINDOWS +#include +#elif defined ZS_HAVE_FREEBSD +#include +#elif defined ZS_HAVE_LINUX || defined ZS_HAVE_SOLARIS || defined ZS_HAVE_OSX +#include +#else +#include +#endif + +namespace zs +{ + + // This class provides RFC 4122 (a Universally Unique IDentifier) + // implementation. + + class uuid_t + { + public: + + uuid_t (); + ~uuid_t (); + + // 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 (); + + private: + + // The length of textual representation of UUID. + enum { uuid_string_len = 36 }; + +#if defined ZS_HAVE_WINDOWS + ::UUID uuid; + char *uuid_str; +#elif defined ZS_HAVE_FREEBSD + ::uuid_t uuid; + char *uuid_str; +#elif defined ZS_HAVE_LINUX || defined ZS_HAVE_SOLARIS || defined ZS_HAVE_OSX + ::uuid_t uuid; + char uuid_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 uuid_buf [uuid_string_len + 1]; +#endif + }; + +} + +#endif diff --git a/src/windows.hpp b/src/windows.hpp new file mode 100644 index 0000000..a4a89ba --- /dev/null +++ b/src/windows.hpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_WINDOWS_HPP_INCLUDED__ +#define __ZS_WINDOWS_HPP_INCLUDED__ + +// The purpose of this header file is to turn on only the items actually needed +// on the windows platform. + +#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 + +#include + +// Enable winsock (not included when WIN32_LEAN_AND_MEAN is defined). +#if(_WIN32_WINNT >= 0x0400) +#include +#include +#else +#include +#endif + +#include +#include +#include + +#endif diff --git a/src/wire.hpp b/src/wire.hpp new file mode 100644 index 0000000..31ebef1 --- /dev/null +++ b/src/wire.hpp @@ -0,0 +1,98 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_WIRE_HPP_INCLUDED__ +#define __ZS_WIRE_HPP_INCLUDED__ + +#include "stdint.hpp" + +namespace zs +{ + + // 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/ypipe.hpp b/src/ypipe.hpp new file mode 100644 index 0000000..5ffd5c9 --- /dev/null +++ b/src/ypipe.hpp @@ -0,0 +1,209 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_YPIPE_HPP_INCLUDED__ +#define __ZS_YPIPE_HPP_INCLUDED__ + +#include "atomic_ptr.hpp" +#include "yqueue.hpp" +#include "platform.hpp" + +namespace zs +{ + + // 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. + // If the template parameter D is set to true, it is quaranteed that + // the pipe will die in a finite time (so that you can swich to some + // other task). If D is set to false, reading from the pipe may result + // in an infinite cycle (if the pipe is continuosly fed by new elements). + // N is granularity of the pipe (how many elements have to be inserted + // till actual memory allocation is required). + + template class ypipe_t + { + public: + + // Initialises the pipe. If 'dead' is set to true, the pipe is + // created in dead state. + inline ypipe_t (bool dead_ = true) : + stop (false) + { + // 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 = &queue.back (); + c.set (dead_ ? NULL : &queue.back ()); + } + + // Following function (write) deliberately copies uninitialised data + // when used with zs_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. + inline void write (const T &value_) + { + // Place the value to the queue, add new terminator element. + queue.back () = value_; + queue.push (); + } + +#ifdef ZMQ_HAVE_OPENVMS +#pragma message restore +#endif + + // Flush the messages 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 == &queue.back ()) + return true; + + // Try to set 'c' to 'back' + if (c.cas (w, &queue.back ()) != 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 (&queue.back ()); + w = &queue.back (); + return false; + } + + // Reader is alive. Nothing special to do now. Just move + // the 'first un-flushed item' pointer to the end of the queue. + w = &queue.back (); + return true; + } + + // Reads an item from the pipe. Returns false if there is no value. + // available. + inline bool read (T *value_) + { + // Was the value was prefetched already? If so, return it. + if (&queue.front () != r) { + *value_ = queue.front (); + queue.pop (); + return true; + } + + // There's no prefetched value, so let us prefetch more values. + // (Note that D is a template parameter. Becaue of that one of + // the following branches will be completely optimised away + // by the compiler.) + if (D) { + + // If one prefetch was already done since last sleeping, + // don't do a new one, rather ask caller to go asleep. + if (stop) { + stop = false; + return false; + } + + // Get new items. Perform the operation in atomic fashion. + r = c.xchg (NULL); + + // If there are no elements prefetched, exit and go asleep. + // During pipe's lifetime r should never be NULL, however, + // during pipe shutdown when retrieving messages from it + // to deallocate them, this can happen. + if (&queue.front () == r || !r) { + stop = false; + return false; + } + else { + + // We want to do only a single prefetch in D scenario + // before going asleep. Thus, we set stop variable to true + // so that we can return false next time the prefetch is + // attempted. + stop = true; + } + } + else { + + // Prefetching in non-D scenario 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, + // during pipe shutdown when retrieving messages from it + // to deallocate them, this can happen. + if (&queue.front () == r || !r) + 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 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; + + // The single contention 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 c; + + // Used only if 'D' template parameter is set to true. If true, + // prefetch was already done since last sleeping and the reader + // should go asleep instead of prefetching once more. + bool stop; + + // Disable copying of ypipe object. + ypipe_t (const ypipe_t&); + void operator = (const ypipe_t&); + }; + +} + +#endif diff --git a/src/ypollset.cpp b/src/ypollset.cpp new file mode 100644 index 0000000..0be6791 --- /dev/null +++ b/src/ypollset.cpp @@ -0,0 +1,56 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "ypollset.hpp" + +zs::ypollset_t::ypollset_t () +{ +} + +void zs::ypollset_t::signal (int signal_) +{ + zs_assert (signal_ >= 0 && signal_ < wait_signal); + if (bits.btsr (signal_, wait_signal)) + sem.post (); +} + +zs::ypollset_t::signals_t zs::ypollset_t::poll () +{ + signals_t result = 0; + while (!result) { + result = bits.izte (signals_t (1) << wait_signal, 0); + if (!result) { + sem.wait (); + result = bits.xchg (0); + } + + // If btsr was really atomic, result would never be 0 at this + // point, i.e. no looping would be possible. However, to + // support even CPU architectures without CAS instruction + // we allow btsr to be composed of two independent atomic + // operation (set and reset). In such case looping can occur + // sporadically. + } + return result; +} + +zs::ypollset_t::signals_t zs::ypollset_t::check () +{ + return bits.xchg (0); +} diff --git a/src/ypollset.hpp b/src/ypollset.hpp new file mode 100644 index 0000000..7c71152 --- /dev/null +++ b/src/ypollset.hpp @@ -0,0 +1,74 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_YPOLLSET_HPP_INCLUDED__ +#define __ZS_YPOLLSET_HPP_INCLUDED__ + +#include "i_signaler.hpp" +#include "simple_semaphore.hpp" +#include "atomic_bitmap.hpp" + +namespace zs +{ + + // ypollset allows for rapid polling for up to constant number of + // different signals each produced by a different thread. The number of + // possible signals is platform-dependent. + + class ypollset_t : public i_signaler + { + public: + + typedef atomic_bitmap_t::bitmap_t signals_t; + + // Create the pollset. + ypollset_t (); + + // Send a signal to the pollset (i_singnaler implementation). + void signal (int signal_); + + // Wait for signal. Returns a set of signals in form of a bitmap. + // Signal with index 0 corresponds to value 1, index 1 to value 2, + // index 2 to value 3 etc. + signals_t poll (); + + // Same as poll, however, if there is no signal available, + // function returns zero immediately instead of waiting for a signal. + signals_t check (); + + private: + + // Wait signal is carried in the most significant bit of integer. + enum {wait_signal = sizeof (signals_t) * 8 - 1}; + + // The bits of the pollset. + atomic_bitmap_t bits; + + // Used by thread waiting for signals to sleep if there are no + // signals available. + simple_semaphore_t sem; + + // Disable copying of ypollset object. + ypollset_t (const ypollset_t&); + void operator = (const ypollset_t&); + }; + +} + +#endif diff --git a/src/yqueue.hpp b/src/yqueue.hpp new file mode 100644 index 0000000..78be17c --- /dev/null +++ b/src/yqueue.hpp @@ -0,0 +1,138 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_YQUEUE_HPP_INCLUDED__ +#define __ZS_YQUEUE_HPP_INCLUDED__ + +#include + +#include "err.hpp" + +namespace zs +{ + + // 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 class yqueue_t + { + public: + + // Create the queue. + inline yqueue_t () + { + begin_chunk = new chunk_t; + zs_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) { + chunk_t *o = begin_chunk; + begin_chunk = begin_chunk->next; + delete o; + if (o == end_chunk) + break; + } + } + + // 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; + + end_chunk->next = new chunk_t; + zs_assert (end_chunk->next); + end_chunk = end_chunk->next; + end_pos = 0; + } + + // 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_pos = 0; + delete o; + } + } + + private: + + // Individual memory chunk to hold N elements. + struct chunk_t + { + T values [N]; + 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; + + // Disable copying of yqueue. + yqueue_t (const yqueue_t&); + void operator = (const yqueue_t&); + }; + +} + +#endif diff --git a/src/zmq_decoder.cpp b/src/zmq_decoder.cpp new file mode 100644 index 0000000..46e4752 --- /dev/null +++ b/src/zmq_decoder.cpp @@ -0,0 +1,78 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "zmq_decoder.hpp" +#include "i_session.hpp" +#include "wire.hpp" + +zs::zmq_decoder_t::zmq_decoder_t () : + destination (NULL) +{ + zs_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); +} + +zs::zmq_decoder_t::~zmq_decoder_t () +{ + zs_msg_close (&in_progress); +} + +void zs::zmq_decoder_t::set_session (i_session *destination_) +{ + destination = destination_; +} + +bool zs::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 { + zs_msg_init_size (&in_progress, *tmpbuf); + next_step (zs_msg_data (&in_progress), *tmpbuf, + &zmq_decoder_t::message_ready); + } + return true; +} + +bool zs::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); + zs_msg_init_size (&in_progress, size); + next_step (zs_msg_data (&in_progress), size, &zmq_decoder_t::message_ready); + return true; +} + +bool zs::zmq_decoder_t::message_ready () +{ + // Message is completely read. Push it further and start reading + // new message. + if (!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..3ab8d6b --- /dev/null +++ b/src/zmq_decoder.hpp @@ -0,0 +1,57 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + + +#ifndef __ZS_ZMQ_DECODER_HPP_INCLUDED__ +#define __ZS_ZMQ_DECODER_HPP_INCLUDED__ + +#include "../include/zs.h" + +#include "decoder.hpp" + +namespace zs +{ + // Decoder for 0MQ backend protocol. Converts data batches into messages. + + class zmq_decoder_t : public decoder_t + { + public: + + zmq_decoder_t (); + ~zmq_decoder_t (); + + void set_session (struct i_session *destination_); + + private: + + bool one_byte_size_ready (); + bool eight_byte_size_ready (); + bool message_ready (); + + struct i_session *destination; + unsigned char tmpbuf [8]; + ::zs_msg 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..9624b69 --- /dev/null +++ b/src/zmq_encoder.cpp @@ -0,0 +1,75 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "zmq_encoder.hpp" +#include "i_session.hpp" +#include "wire.hpp" + +zs::zmq_encoder_t::zmq_encoder_t () : + source (NULL) +{ + zs_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); +} + +zs::zmq_encoder_t::~zmq_encoder_t () +{ + zs_msg_close (&in_progress); +} + +void zs::zmq_encoder_t::set_session (i_session *source_) +{ + source = source_; +} + +bool zs::zmq_encoder_t::size_ready () +{ + // Write message body into the buffer. + next_step (zs_msg_data (&in_progress), zs_msg_size (&in_progress), + &zmq_encoder_t::message_ready, false); + return true; +} + +bool zs::zmq_encoder_t::message_ready () +{ + // Read new message from the dispatcher. 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->read (&in_progress)) { + return false; + } + size_t size = zs_msg_size (&in_progress); + + // 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. + if (size < 255) { + tmpbuf [0] = (unsigned char) size; + next_step (tmpbuf, 1, &zmq_encoder_t::size_ready, true); + } + else { + tmpbuf [0] = 0xff; + put_uint64 (tmpbuf + 1, size); + next_step (tmpbuf, 9, &zmq_encoder_t::size_ready, true); + } + return true; +} diff --git a/src/zmq_encoder.hpp b/src/zmq_encoder.hpp new file mode 100644 index 0000000..37c4aee --- /dev/null +++ b/src/zmq_encoder.hpp @@ -0,0 +1,54 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ZMQ_ENCODER_HPP_INCLUDED__ +#define __ZS_ZMQ_ENCODER_HPP_INCLUDED__ + +#include "../include/zs.h" + +#include "encoder.hpp" + +namespace zs +{ + // Encoder for 0MQ backend protocol. Converts messages into data batches. + + class zmq_encoder_t : public encoder_t + { + public: + + zmq_encoder_t (); + ~zmq_encoder_t (); + + void set_session (struct i_session *source_); + + private: + + bool size_ready (); + bool message_ready (); + + struct i_session *source; + ::zs_msg in_progress; + unsigned char tmpbuf [9]; + + zmq_encoder_t (const zmq_encoder_t&); + void operator = (const zmq_encoder_t&); + }; +} + +#endif diff --git a/src/zmq_tcp_engine.cpp b/src/zmq_tcp_engine.cpp new file mode 100644 index 0000000..0f55808 --- /dev/null +++ b/src/zmq_tcp_engine.cpp @@ -0,0 +1,185 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "zmq_tcp_engine.hpp" +#include "config.hpp" +#include "i_session.hpp" +#include "err.hpp" + +zs::zmq_tcp_engine_t::zmq_tcp_engine_t (fd_t fd_) : + poller (NULL), + session (NULL), + terminating (false), + insize (0), + inpos (0), + outsize (0), + outpos (0) +{ + // Allocate read & write buffer. + inbuf = new unsigned char [in_batch_size]; + zs_assert (inbuf); + outbuf = new unsigned char [out_batch_size]; + zs_assert (outbuf); + + // Attach the socket. + int rc = socket.open (fd_); + zs_assert (rc == 0); +} + +void zs::zmq_tcp_engine_t::attach (i_poller *poller_, i_session *session_) +{ + zs_assert (!poller); + poller = poller_; + zs_assert (!session); + session = session_; + encoder.set_session (session); + decoder.set_session (session); + + // Let session know we are here. + session->set_engine (this); + + // Register the engine with the polling thread. + handle = poller->add_fd (socket.get_fd (), this); + poller->set_pollin (handle); + poller->set_pollout (handle); + + // Flush any pending inbound messages. + in_event (); +} + +void zs::zmq_tcp_engine_t::detach () +{ + zs_assert (poller); + poller->rm_fd (handle); + poller = NULL; + zs_assert (session); + session->set_engine (NULL); + session = NULL; + encoder.set_session (NULL); + decoder.set_session (NULL); +} + +void zs::zmq_tcp_engine_t::revive () +{ + zs_assert (poller); + poller->set_pollout (handle); +} + +void zs::zmq_tcp_engine_t::schedule_terminate () +{ + terminating = true; +} + +void zs::zmq_tcp_engine_t::terminate () +{ + delete this; +} + +void zs::zmq_tcp_engine_t::shutdown () +{ + delete this; +} + +zs::zmq_tcp_engine_t::~zmq_tcp_engine_t () +{ + detach (); + delete [] outbuf; + delete [] inbuf; +} + +void zs::zmq_tcp_engine_t::in_event () +{ + // If there's no data to process in the buffer, read new data. + if (inpos == insize) { + + // Read as much data as possible to the read buffer. + insize = socket.read (inbuf, in_batch_size); + inpos = 0; + + // Check whether the peer has closed the connection. + if (insize == -1) { + insize = 0; + error (); + return; + } + } + + // Following code should be executed even if there's not a single byte in + // the buffer. There still can be a decoded messages stored in the decoder. + + // Push the data to the decoder. + int nbytes = decoder.write (inbuf + inpos, insize - inpos); + + // Adjust read position. Stop polling for input if we got stuck. + inpos += nbytes; + if (inpos < insize) + poller->reset_pollin (handle); + + // If at least one byte was processed, flush all messages the decoder + // may have produced. If engine is disconnected from session, no need + // to flush the messages. It's important that flush is called at the + // very end of in_event as it may invoke in_event itself. + if (nbytes > 0 && session) + session->flush (); +} + +void zs::zmq_tcp_engine_t::out_event () +{ + // If write buffer is empty, try to read new data from the encoder. + if (outpos == outsize) { + + outsize = encoder.read (outbuf, out_batch_size); + outpos = 0; + + // If there are no more pipes, engine can be deallocated. + if (terminating) { + terminate (); + return; + } + + // If there is no data to send, stop polling for output. + if (outsize == 0) + poller->reset_pollout (handle); + } + + // If there are any data to write in write buffer, write as much as + // possible to the socket. + if (outpos < outsize) { + int nbytes = socket.write (outbuf + outpos, outsize - outpos); + + // Handle problems with the connection. + if (nbytes == -1) { + error (); + return; + } + + outpos += nbytes; + } +} + +void zs::zmq_tcp_engine_t::timer_event () +{ + zs_assert (false); +} + +void zs::zmq_tcp_engine_t::error () +{ + zs_assert (false); +} + diff --git a/src/zmq_tcp_engine.hpp b/src/zmq_tcp_engine.hpp new file mode 100644 index 0000000..fb1413c --- /dev/null +++ b/src/zmq_tcp_engine.hpp @@ -0,0 +1,92 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#ifndef __ZS_ZMQ_TCP_ENGINE_HPP_INCLUDED__ +#define __ZS_ZMQ_TCP_ENGINE_HPP_INCLUDED__ + +#include "i_engine.hpp" +#include "i_poller.hpp" +#include "i_poll_events.hpp" +#include "fd.hpp" +#include "tcp_socket.hpp" +#include "zmq_encoder.hpp" +#include "zmq_decoder.hpp" + +namespace zs +{ + + class zmq_tcp_engine_t : public i_engine, public i_poll_events + { + public: + + zmq_tcp_engine_t (fd_t fd_); + + // i_engine implementation. + void attach (struct i_poller *poller_, struct i_session *session_); + void detach (); + void revive (); + void schedule_terminate (); + void terminate (); + void shutdown (); + + // i_poll_events implementation. + void in_event (); + void out_event (); + void timer_event (); + + private: + + void error (); + + // Clean-up. + ~zmq_tcp_engine_t (); + + // The underlying TCP socket. + tcp_socket_t socket; + + // Handle associated with the socket. + handle_t handle; + + // I/O thread that the engine runs in. + i_poller *poller; + + // Reference to the associated session object. + i_session *session; + + // If true, engine should terminate itself as soon as possible. + bool terminating; + + unsigned char *inbuf; + int insize; + int inpos; + + unsigned char *outbuf; + int outsize; + int outpos; + + zmq_encoder_t encoder; + zmq_decoder_t decoder; + + zmq_tcp_engine_t (const zmq_tcp_engine_t&); + void operator = (const zmq_tcp_engine_t&); + }; + +} + +#endif diff --git a/src/zs.cpp b/src/zs.cpp new file mode 100644 index 0000000..ca05db3 --- /dev/null +++ b/src/zs.cpp @@ -0,0 +1,222 @@ +/* + Copyright (c) 2007-2009 FastMQ Inc. + + 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 . +*/ + +#include "../include/zs.h" + +#include +#include +#include + +#include "i_api.hpp" +#include "err.hpp" +#include "dispatcher.hpp" +#include "msg.hpp" + +int zs_msg_init (zs_msg *msg_) +{ + msg_->content = (zs_msg_content*) ZS_VSM; + msg_->vsm_size = 0; + return 0; +} + +int zs_msg_init_size (zs_msg *msg_, size_t size_) +{ + if (size_ <= ZS_MAX_VSM_SIZE) { + msg_->content = (zs_msg_content*) ZS_VSM; + msg_->vsm_size = (uint16_t) size_; + } + else { + msg_->content = (zs_msg_content*) malloc (sizeof (zs_msg_content) + + size_); + if (!msg_->content) { + errno = ENOMEM; + return -1; + } + msg_->shared = 0; + + msg_->content->data = (void*) (msg_->content + 1); + msg_->content->size = size_; + msg_->content->ffn = NULL; + new (&msg_->content->refcnt) zs::atomic_counter_t (); + } + return 0; +} + +int zs_msg_init_data (zs_msg *msg_, void *data_, size_t size_, zs_free_fn *ffn_) +{ + msg_->shared = 0; + msg_->content = (zs_msg_content*) malloc (sizeof (zs_msg_content)); + zs_assert (msg_->content); + msg_->content->data = data_; + msg_->content->size = size_; + msg_->content->ffn = ffn_; + new (&msg_->content->refcnt) zs::atomic_counter_t (); + return 0; +} + +int zs_msg_close (zs_msg *msg_) +{ + // For VSMs and delimiters there are no resources to free + if (msg_->content == (zs_msg_content*) ZS_DELIMITER || + msg_->content == (zs_msg_content*) ZS_VSM || + msg_->content == (zs_msg_content*) ZS_GAP) + return 0; + + // If the content is not shared, or if it is shared and the reference + // count has dropped to zero, deallocate it. + if (!msg_->shared || !msg_->content->refcnt.sub (1)) { + + // We used "placement new" operator to initialize the reference + // counter so we call its destructor now. + msg_->content->refcnt.~atomic_counter_t (); + + if (msg_->content->ffn) + msg_->content->ffn (msg_->content->data); + free (msg_->content); + } + + return 0; +} + +int zs_msg_move (zs_msg *dest_, zs_msg *src_) +{ + zs_msg_close (dest_); + *dest_ = *src_; + zs_msg_init (src_); + return 0; +} + +int zs_msg_copy (zs_msg *dest_, zs_msg *src_) +{ + zs_msg_close (dest_); + + // VSMs and delimiters require no special handling. + if (src_->content != + (zs_msg_content*) ZS_DELIMITER && + src_->content != (zs_msg_content*) ZS_VSM && + src_->content != (zs_msg_content*) ZS_GAP) { + + // One reference is added to shared messages. Non-shared messages + // are turned into shared messages and reference count is set to 2. + if (src_->shared) + src_->content->refcnt.add (1); + else { + src_->shared = true; + src_->content->refcnt.set (2); + } + } + + *dest_ = *src_; + return 0; +} + +void *zs_msg_data (zs_msg *msg_) +{ + if (msg_->content == (zs_msg_content*) ZS_VSM) + return msg_->vsm_data; + if (msg_->content == + (zs_msg_content*) ZS_DELIMITER || + msg_->content == (zs_msg_content*) ZS_GAP) + return NULL; + return msg_->content->data; +} + +size_t zs_msg_size (zs_msg *msg_) +{ + if (msg_->content == (zs_msg_content*) ZS_VSM) + return msg_->vsm_size; + if (msg_->content == + (zs_msg_content*) ZS_DELIMITER || + msg_->content == (zs_msg_content*) ZS_GAP) + return 0; + return msg_->content->size; +} + +int zs_msg_type (zs_msg *msg_) +{ + // If it's a genuine message, return 0. + if (msg_->content >= (zs_msg_content*) ZS_VSM) + return 0; + + // Trick the compiler to believe that content is an integer. + unsigned char *offset = 0; + return (((const unsigned char*) msg_->content) - offset); +} + +void *zs_init (int app_threads_, int io_threads_) +{ + // There should be at least a single thread managed by the dispatcher. + if (app_threads_ < 0 || io_threads_ < 0 || + app_threads_ + io_threads_ == 0) { + errno = EINVAL; + return NULL; + } + + zs::dispatcher_t *dispatcher = + new zs::dispatcher_t (app_threads_, io_threads_); + zs_assert (dispatcher); + return (void*) dispatcher; +} + +int zs_term (void *context_) +{ + ((zs::dispatcher_t*) context_)->shutdown (); + return 0; +} + +void *zs_socket (void *context_, int type_) +{ + return (void*) (((zs::dispatcher_t*) context_)->create_socket (type_)); +} + +int zs_close (void *s_) +{ + ((zs::i_api*) s_)->close (); + return 0; +} + +int zs_bind (void *s_, const char *addr_, zs_opts *opts_) +{ + return (((zs::i_api*) s_)->bind (addr_, opts_)); +} + +int zs_connect (void *s_, const char *addr_, zs_opts *opts_) +{ + return (((zs::i_api*) s_)->connect (addr_, opts_)); +} + +int zs_subscribe (void *s_, const char *criteria_) +{ + return (((zs::i_api*) s_)->subscribe (criteria_)); +} + +int zs_send (void *s_, zs_msg *msg_, int flags_) +{ + return (((zs::i_api*) s_)->send (msg_, flags_)); +} + +int zs_flush (void *s_) +{ + return (((zs::i_api*) s_)->flush ()); +} + +int zs_recv (void *s_, zs_msg *msg_, int flags_) +{ + return (((zs::i_api*) s_)->recv (msg_, flags_)); +} -- cgit v1.2.3