mirror of
https://github.com/zeromq/libzmq.git
synced 2025-03-09 07:16:04 +00:00
initial commit
This commit is contained in:
commit
4ed70a9302
4
Makefile.am
Normal file
4
Makefile.am
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
include_HEADERS = include/zs.h include/zs.hpp
|
||||||
|
|
||||||
|
SUBDIRS = src examples
|
||||||
|
DIST_SUBDIRS = src examples
|
29
autogen.sh
Executable file
29
autogen.sh
Executable file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# 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
|
188
configure.in
Normal file
188
configure.in
Normal file
@ -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 <atomic.h>]],
|
||||||
|
[[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([])
|
||||||
|
|
2
examples/Makefile.am
Normal file
2
examples/Makefile.am
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
SUBDIRS = chat
|
||||||
|
DIST_SUBDIRS = chat
|
15
examples/chat/Makefile.am
Normal file
15
examples/chat/Makefile.am
Normal file
@ -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
|
74
examples/chat/chatroom.cpp
Normal file
74
examples/chat/chatroom.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <zs.hpp>
|
||||||
|
|
||||||
|
int main (int argc, const char *argv [])
|
||||||
|
{
|
||||||
|
// Check the command line syntax
|
||||||
|
if (argc != 3) {
|
||||||
|
cerr << "usage: chatroom <in-interface> <out-interface>" << 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);
|
||||||
|
}
|
||||||
|
}
|
56
examples/chat/display.cpp
Normal file
56
examples/chat/display.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <zs.hpp>
|
||||||
|
|
||||||
|
int main (int argc, const char *argv [])
|
||||||
|
{
|
||||||
|
// Check the command line syntax.
|
||||||
|
if (argc != 2) {
|
||||||
|
cerr << "usage: display <chatroom-out-address>" << 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;
|
||||||
|
}
|
||||||
|
}
|
61
examples/chat/prompt.cpp
Normal file
61
examples/chat/prompt.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
#include <zs.hpp>
|
||||||
|
|
||||||
|
int main (int argc, const char *argv [])
|
||||||
|
{
|
||||||
|
// Check the command line syntax.
|
||||||
|
if (argc != 3) {
|
||||||
|
cerr << "usage: prompt <chatroom-in-address> <user name>" << 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);
|
||||||
|
}
|
||||||
|
}
|
206
include/zs.h
Normal file
206
include/zs.h
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZSOCKETS_H_INCLUDED__
|
||||||
|
#define __ZSOCKETS_H_INCLUDED__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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
|
231
include/zs.hpp
Normal file
231
include/zs.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZSOCKETS_HPP_INCLUDED__
|
||||||
|
#define __ZSOCKETS_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "zs.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
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
|
120
src/Makefile.am
Normal file
120
src/Makefile.am
Normal file
@ -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
|
||||||
|
|
||||||
|
|
221
src/app_thread.cpp
Normal file
221
src/app_thread.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../include/zs.h"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_WINDOWS
|
||||||
|
#include "windows.hpp"
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
src/app_thread.hpp
Normal file
95
src/app_thread.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_APP_THREAD_HPP_INCLUDED__
|
||||||
|
#define __ZS_APP_THREAD_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 <class session_t*> 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
|
310
src/atomic.hpp
Normal file
310
src/atomic.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <typename T>
|
||||||
|
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 <typename T>
|
||||||
|
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 <typename T>
|
||||||
|
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
|
286
src/atomic_bitmap.hpp
Normal file
286
src/atomic_bitmap.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <atomic.h>
|
||||||
|
#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
|
197
src/atomic_counter.hpp
Normal file
197
src/atomic_counter.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#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 <atomic.h>
|
||||||
|
#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
|
189
src/atomic_ptr.hpp
Normal file
189
src/atomic_ptr.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#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 <atomic.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace zs
|
||||||
|
{
|
||||||
|
|
||||||
|
// This class encapsulates several atomic operations on pointers.
|
||||||
|
|
||||||
|
template <typename T> class atomic_ptr_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Initialise atomic pointer
|
||||||
|
inline atomic_ptr_t ()
|
||||||
|
{
|
||||||
|
ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy atomic pointer
|
||||||
|
inline ~atomic_ptr_t ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set value of atomic pointer in a non-threadsafe way
|
||||||
|
// Use this function only when you are sure that at most one
|
||||||
|
// thread is accessing the pointer at the moment.
|
||||||
|
inline void set (T *ptr_)
|
||||||
|
{
|
||||||
|
this->ptr = ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform atomic 'exchange pointers' operation. Pointer is set
|
||||||
|
// to the 'val' value. Old value is returned.
|
||||||
|
inline T *xchg (T *val_)
|
||||||
|
{
|
||||||
|
#if defined 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
|
98
src/command.hpp
Normal file
98
src/command.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
71
src/config.hpp
Normal file
71
src/config.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
189
src/connecter.cpp
Normal file
189
src/connecter.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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.
|
||||||
|
}
|
99
src/connecter.hpp
Normal file
99
src/connecter.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_CONNECTER_HPP_INCLUDED__
|
||||||
|
#define __ZS_CONNECTER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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
|
155
src/data_distributor.cpp
Normal file
155
src/data_distributor.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
70
src/data_distributor.hpp
Normal file
70
src/data_distributor.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DATA_DISTRIBUTOR_HPP_INCLUDED__
|
||||||
|
#define __ZS_DATA_DISTRIBUTOR_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <i_demux.hpp>
|
||||||
|
|
||||||
|
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 <class pipe_writer_t*> pipes_t;
|
||||||
|
pipes_t pipes;
|
||||||
|
|
||||||
|
data_distributor_t (const data_distributor_t&);
|
||||||
|
void operator = (const data_distributor_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
101
src/decoder.hpp
Normal file
101
src/decoder.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DECODER_HPP_INCLUDED__
|
||||||
|
#define __ZS_DECODER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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 <typename T> 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 <T*> (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
|
224
src/devpoll.cpp
Normal file
224
src/devpoll.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_SOLARIS || defined ZS_HAVE_HPUX
|
||||||
|
|
||||||
|
#include <sys/devpoll.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "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
|
110
src/devpoll.hpp
Normal file
110
src/devpoll.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DEVPOLL_HPP_INCLUDED__
|
||||||
|
#define __ZS_DEVPOLL_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_SOLARIS || ZS_HAVE_HPUX
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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_entry_t> fd_table;
|
||||||
|
|
||||||
|
typedef std::vector <fd_t> pending_list_t;
|
||||||
|
pending_list_t pending_list;
|
||||||
|
|
||||||
|
// Pollset manipulation function.
|
||||||
|
void devpoll_ctl (fd_t fd_, short events_);
|
||||||
|
|
||||||
|
// List of all the engines waiting for the timer event.
|
||||||
|
typedef std::vector <struct i_poll_events*> timers_t;
|
||||||
|
timers_t timers;
|
||||||
|
|
||||||
|
// If true, thread is in the process of shutting down.
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
// Handle of the physical thread doing the I/O work.
|
||||||
|
thread_t worker;
|
||||||
|
|
||||||
|
// Load of the poller. Currently number of file descriptors
|
||||||
|
// registered with the poller.
|
||||||
|
atomic_counter_t load;
|
||||||
|
|
||||||
|
devpoll_t (const devpoll_t&);
|
||||||
|
void operator = (const devpoll_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
266
src/dispatcher.cpp
Normal file
266
src/dispatcher.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
||||||
|
|
170
src/dispatcher.hpp
Normal file
170
src/dispatcher.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DISPATCHER_HPP_INCLUDED__
|
||||||
|
#define __ZS_DISPATCHER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 <class app_thread_t*> app_threads_t;
|
||||||
|
app_threads_t app_threads;
|
||||||
|
|
||||||
|
// I/O threads.
|
||||||
|
typedef std::vector <class io_thread_t*> io_threads_t;
|
||||||
|
io_threads_t io_threads;
|
||||||
|
|
||||||
|
// Signalers for both application and I/O threads.
|
||||||
|
std::vector <i_signaler*> signalers;
|
||||||
|
|
||||||
|
// Pipe to hold the commands.
|
||||||
|
typedef ypipe_t <command_t, true,
|
||||||
|
command_pipe_granularity> 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 <pipe_info_t> 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 <std::string, class session_t*> 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
|
||||||
|
|
111
src/dummy_aggregator.cpp
Normal file
111
src/dummy_aggregator.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
73
src/dummy_aggregator.hpp
Normal file
73
src/dummy_aggregator.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DUMMY_AGGREGATOR_HPP_INCLUDED__
|
||||||
|
#define __ZS_DUMMY_AGGREGATOR_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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
|
85
src/dummy_distributor.cpp
Normal file
85
src/dummy_distributor.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
||||||
|
|
68
src/dummy_distributor.hpp
Normal file
68
src/dummy_distributor.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_DUMMY_DISTRIBUTOR_HPP_INCLUDED__
|
||||||
|
#define __ZS_DUMMY_DISTRIBUTOR_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <i_demux.hpp>
|
||||||
|
|
||||||
|
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
|
108
src/encoder.hpp
Normal file
108
src/encoder.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_ENCODER_HPP_INCLUDED__
|
||||||
|
#define __ZS_ENCODER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
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 <typename T> 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 <T*> (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
|
214
src/epoll.cpp
Normal file
214
src/epoll.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_LINUX
|
||||||
|
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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
|
107
src/epoll.hpp
Normal file
107
src/epoll.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_EPOLL_HPP_INCLUDED__
|
||||||
|
#define __ZS_EPOLL_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_LINUX
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
#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 <poll_entry_t*> retired_t;
|
||||||
|
retired_t retired;
|
||||||
|
|
||||||
|
// List of all the engines waiting for the timer event.
|
||||||
|
typedef std::vector <struct i_poll_events*> timers_t;
|
||||||
|
timers_t timers;
|
||||||
|
|
||||||
|
// If true, thread is in the process of shutting down.
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
// Handle of the physical thread doing the I/O work.
|
||||||
|
thread_t worker;
|
||||||
|
|
||||||
|
// Load of the poller. Currently number of file descriptors
|
||||||
|
// registered with the poller.
|
||||||
|
atomic_counter_t load;
|
||||||
|
|
||||||
|
epoll_t (const epoll_t&);
|
||||||
|
void operator = (const epoll_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
146
src/err.cpp
Normal file
146
src/err.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
90
src/err.hpp
Normal file
90
src/err.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_ERR_HPP_INCLUDED__
|
||||||
|
#define __ZS_ERR_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_WINDOWS
|
||||||
|
#include "windows.hpp"
|
||||||
|
#else
|
||||||
|
#include <netdb.h>
|
||||||
|
#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
|
143
src/fair_aggregator.cpp
Normal file
143
src/fair_aggregator.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
77
src/fair_aggregator.hpp
Normal file
77
src/fair_aggregator.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_FAIR_AGGREGATOR_HPP_INCLUDED__
|
||||||
|
#define __ZS_FAIR_AGGREGATOR_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 <class pipe_reader_t*> 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
|
44
src/fd.hpp
Normal file
44
src/fd.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
278
src/fd_signaler.cpp
Normal file
278
src/fd_signaler.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fd_signaler.hpp"
|
||||||
|
#include "platform.hpp"
|
||||||
|
#include "err.hpp"
|
||||||
|
#include "fd.hpp"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_OPENVMS
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#elif defined ZS_HAVE_WINDOWS
|
||||||
|
#include "windows.hpp"
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_EVENTFD
|
||||||
|
|
||||||
|
#include <sys/eventfd.h>
|
||||||
|
|
||||||
|
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 <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
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
|
||||||
|
|
92
src/fd_signaler.hpp
Normal file
92
src/fd_signaler.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
39
src/i_api.hpp
Normal file
39
src/i_api.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
56
src/i_demux.hpp
Normal file
56
src/i_demux.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
53
src/i_engine.hpp
Normal file
53
src/i_engine.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
59
src/i_mux.hpp
Normal file
59
src/i_mux.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
45
src/i_poll_events.hpp
Normal file
45
src/i_poll_events.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
89
src/i_poller.hpp
Normal file
89
src/i_poller.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
37
src/i_session.hpp
Normal file
37
src/i_session.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
38
src/i_signaler.hpp
Normal file
38
src/i_signaler.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
38
src/i_thread.hpp
Normal file
38
src/i_thread.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
37
src/io_object.cpp
Normal file
37
src/io_object.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
51
src/io_object.hpp
Normal file
51
src/io_object.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
177
src/io_thread.cpp
Normal file
177
src/io_thread.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ();
|
||||||
|
}
|
99
src/io_thread.hpp
Normal file
99
src/io_thread.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_IO_THREAD_HPP_INCLUDED__
|
||||||
|
#define __ZS_IO_THREAD_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 <class session_t*> sessions_t;
|
||||||
|
sessions_t sessions;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
310
src/ip.cpp
Normal file
310
src/ip.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "ip.hpp"
|
||||||
|
#include "platform.hpp"
|
||||||
|
#include "err.hpp"
|
||||||
|
#include "stdint.hpp"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_SOLARIS
|
||||||
|
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// On Solaris platform, network interface name can be queried by ioctl.
|
||||||
|
static int resolve_nic_name (in_addr* addr_, char const *interface_)
|
||||||
|
{
|
||||||
|
// * 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 <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#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 <ifaddrs.h>
|
||||||
|
|
||||||
|
// On these platforms, network interface name can be queried
|
||||||
|
// using getifaddrs function.
|
||||||
|
static int resolve_nic_name (in_addr* addr_, char const *interface_)
|
||||||
|
{
|
||||||
|
// * 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;
|
||||||
|
}
|
47
src/ip.hpp
Normal file
47
src/ip.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_IP_HPP_INCLUDED__
|
||||||
|
#define __ZS_IP_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_WINDOWS
|
||||||
|
#include "windows.hpp"
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace zs
|
||||||
|
{
|
||||||
|
|
||||||
|
// Resolves network interface name in <nic-name>:<port> 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 <hostname>:<port-number> 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
|
214
src/kqueue.cpp
Normal file
214
src/kqueue.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#if defined ZS_HAVE_FREEBSD || defined ZS_HAVE_OPENBSD || defined ZS_HAVE_OSX
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/event.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#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
|
112
src/kqueue.hpp
Normal file
112
src/kqueue.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <vector>
|
||||||
|
|
||||||
|
#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 <poll_entry_t*> retired_t;
|
||||||
|
retired_t retired;
|
||||||
|
|
||||||
|
// List of all the engines waiting for the timer event.
|
||||||
|
typedef std::vector <struct i_poll_events*> timers_t;
|
||||||
|
timers_t timers;
|
||||||
|
|
||||||
|
// If true, thread is in the process of shutting down.
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
// Handle of the physical thread doing the I/O work.
|
||||||
|
thread_t worker;
|
||||||
|
|
||||||
|
// Load of the poller. Currently number of file descriptors
|
||||||
|
// registered with the poller.
|
||||||
|
atomic_counter_t load;
|
||||||
|
|
||||||
|
kqueue_t (const kqueue_t&);
|
||||||
|
void operator = (const kqueue_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
170
src/listener.cpp
Normal file
170
src/listener.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
110
src/listener.hpp
Normal file
110
src/listener.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_LISTENER_HPP_INCLUDED__
|
||||||
|
#define __ZS_LISTENER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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 <std::string, class session_t*> 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 <class session_stub_t*> 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
|
130
src/load_balancer.cpp
Normal file
130
src/load_balancer.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
73
src/load_balancer.hpp
Normal file
73
src/load_balancer.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_LOAD_BALANCER_HPP_INCLUDED__
|
||||||
|
#define __ZS_LOAD_BALANCER_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <i_demux.hpp>
|
||||||
|
|
||||||
|
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 <class pipe_writer_t*> 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
|
49
src/msg.hpp
Normal file
49
src/msg.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_MSG_HPP_INCLUDE__
|
||||||
|
#define __ZS_MSG_HPP_INCLUDE__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#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
|
116
src/mutex.hpp
Normal file
116
src/mutex.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <pthread.h>
|
||||||
|
|
||||||
|
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
|
294
src/object.cpp
Normal file
294
src/object.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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_);
|
||||||
|
}
|
||||||
|
|
105
src/object.hpp
Normal file
105
src/object.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
29
src/p2p.cpp
Normal file
29
src/p2p.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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_)
|
||||||
|
{
|
||||||
|
}
|
42
src/p2p.hpp
Normal file
42
src/p2p.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
47
src/pipe.cpp
Normal file
47
src/pipe.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pipe.hpp"
|
||||||
|
|
||||||
|
zs::pipe_t::pipe_t () :
|
||||||
|
ypipe_t <zs_msg, false, message_pipe_granularity> (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;
|
||||||
|
}
|
57
src/pipe.hpp
Normal file
57
src/pipe.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <zs_msg, false, message_pipe_granularity>
|
||||||
|
{
|
||||||
|
// 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
|
118
src/pipe_reader.cpp
Normal file
118
src/pipe_reader.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
89
src/pipe_reader.hpp
Normal file
89
src/pipe_reader.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
120
src/pipe_writer.cpp
Normal file
120
src/pipe_writer.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
88
src/pipe_writer.hpp
Normal file
88
src/pipe_writer.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
210
src/platform.hpp.in
Normal file
210
src/platform.hpp.in
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
/* src/platform.hpp.in. Generated from configure.in by autoheader. */
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <arpa/inet.h> header file. */
|
||||||
|
#undef HAVE_ARPA_INET_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */
|
||||||
|
#undef HAVE_DLFCN_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <errno.h> header file. */
|
||||||
|
#undef HAVE_ERRNO_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `freeifaddrs' function. */
|
||||||
|
#undef HAVE_FREEIFADDRS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `getifaddrs' function. */
|
||||||
|
#undef HAVE_GETIFADDRS
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `gettimeofday' function. */
|
||||||
|
#undef HAVE_GETTIMEOFDAY
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <ifaddrs.h> header file. */
|
||||||
|
#undef HAVE_IFADDRS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||||
|
#undef HAVE_INTTYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `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 <limits.h> 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 <memory.h> header file. */
|
||||||
|
#undef HAVE_MEMORY_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `memset' function. */
|
||||||
|
#undef HAVE_MEMSET
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/in.h> header file. */
|
||||||
|
#undef HAVE_NETINET_IN_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <netinet/tcp.h> header file. */
|
||||||
|
#undef HAVE_NETINET_TCP_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `perror' function. */
|
||||||
|
#undef HAVE_PERROR
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `socket' function. */
|
||||||
|
#undef HAVE_SOCKET
|
||||||
|
|
||||||
|
/* Define to 1 if stdbool.h conforms to C99. */
|
||||||
|
#undef HAVE_STDBOOL_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stddef.h> header file. */
|
||||||
|
#undef HAVE_STDDEF_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdint.h> header file. */
|
||||||
|
#undef HAVE_STDINT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <strings.h> header file. */
|
||||||
|
#undef HAVE_STRINGS_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <string.h> header file. */
|
||||||
|
#undef HAVE_STRING_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/eventfd.h> header file. */
|
||||||
|
#undef HAVE_SYS_EVENTFD_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||||
|
#undef HAVE_SYS_SOCKET_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||||
|
#undef HAVE_SYS_STAT_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/time.h> header file. */
|
||||||
|
#undef HAVE_SYS_TIME_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||||
|
#undef HAVE_SYS_TYPES_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
|
#undef HAVE_UNISTD_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the <windows.h> header file. */
|
||||||
|
#undef HAVE_WINDOWS_H
|
||||||
|
|
||||||
|
/* Define to 1 if the system has the type `_Bool'. */
|
||||||
|
#undef HAVE__BOOL
|
||||||
|
|
||||||
|
/* Define to 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 <sys/time.h> and <time.h>. */
|
||||||
|
#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 <sys/synch.h>,
|
||||||
|
<pthread.h>, or <semaphore.h> is not used. If the typedef was allowed, the
|
||||||
|
#define below would cause a syntax error. */
|
||||||
|
#undef _UINT32_T
|
||||||
|
|
||||||
|
/* Define to empty if `const' does not conform to ANSI C. */
|
||||||
|
#undef const
|
||||||
|
|
||||||
|
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||||
|
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||||
|
#ifndef __cplusplus
|
||||||
|
#undef inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Define to rpl_malloc if the replacement function should be used. */
|
||||||
|
#undef malloc
|
||||||
|
|
||||||
|
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||||
|
#undef size_t
|
||||||
|
|
||||||
|
/* Define to `int' if <sys/types.h> does not define. */
|
||||||
|
#undef ssize_t
|
||||||
|
|
||||||
|
/* Define to the type of an unsigned integer type of width exactly 32 bits if
|
||||||
|
such a type exists and the standard includes do not define it. */
|
||||||
|
#undef uint32_t
|
||||||
|
|
||||||
|
/* Define to empty if the keyword `volatile' does not work. Warning: valid
|
||||||
|
code using `volatile' can become incorrect without. Disable with care. */
|
||||||
|
#undef volatile
|
205
src/poll.cpp
Normal file
205
src/poll.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "poll.hpp"
|
||||||
|
#include "err.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "i_poll_events.hpp"
|
||||||
|
|
||||||
|
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
|
112
src/poll.hpp
Normal file
112
src/poll.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <poll.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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_entry_t> fd_table;
|
||||||
|
|
||||||
|
// Pollset to pass to the poll function.
|
||||||
|
typedef std::vector <pollfd> pollset_t;
|
||||||
|
pollset_t pollset;
|
||||||
|
|
||||||
|
// If true, there's at least one retired event source.
|
||||||
|
bool retired;
|
||||||
|
|
||||||
|
// List of all the engines waiting for the timer event.
|
||||||
|
typedef std::vector <struct i_poll_events*> timers_t;
|
||||||
|
timers_t timers;
|
||||||
|
|
||||||
|
// If true, thread is in the process of shutting down.
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
// Handle of the physical thread doing the I/O work.
|
||||||
|
thread_t worker;
|
||||||
|
|
||||||
|
// Load of the poller. Currently number of file descriptors
|
||||||
|
// registered with the poller.
|
||||||
|
atomic_counter_t load;
|
||||||
|
|
||||||
|
poll_t (const poll_t&);
|
||||||
|
void operator = (const poll_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
38
src/pub.cpp
Normal file
38
src/pub.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
45
src/pub.hpp
Normal file
45
src/pub.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
29
src/rep.cpp
Normal file
29
src/rep.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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_)
|
||||||
|
{
|
||||||
|
}
|
42
src/rep.hpp
Normal file
42
src/rep.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
29
src/req.cpp
Normal file
29
src/req.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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_)
|
||||||
|
{
|
||||||
|
}
|
42
src/req.hpp
Normal file
42
src/req.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
76
src/safe_object.cpp
Normal file
76
src/safe_object.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 ()
|
||||||
|
{
|
||||||
|
}
|
68
src/safe_object.hpp
Normal file
68
src/safe_object.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
236
src/select.cpp
Normal file
236
src/select.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_WINDOWS
|
||||||
|
#include "winsock2.h"
|
||||||
|
#elif defined ZS_HAVE_HPUX
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#elif defined ZS_HAVE_OPENVMS
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#else
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "select.hpp"
|
||||||
|
#include "err.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "i_poll_events.hpp"
|
||||||
|
|
||||||
|
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 ();
|
||||||
|
}
|
122
src/select.hpp
Normal file
122
src/select.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_SELECT_HPP_INCLUDED__
|
||||||
|
#define __ZS_SELECT_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_WINDOWS
|
||||||
|
#include "winsock2.h"
|
||||||
|
#elif defined ZS_HAVE_OPENVMS
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#else
|
||||||
|
#include <sys/select.h>
|
||||||
|
#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_entry_t> fd_set_t;
|
||||||
|
fd_set_t fds;
|
||||||
|
|
||||||
|
fd_set source_set_in;
|
||||||
|
fd_set source_set_out;
|
||||||
|
fd_set source_set_err;
|
||||||
|
|
||||||
|
fd_set readfds;
|
||||||
|
fd_set writefds;
|
||||||
|
fd_set exceptfds;
|
||||||
|
|
||||||
|
// Maximum file descriptor.
|
||||||
|
fd_t maxfd;
|
||||||
|
|
||||||
|
// If true, at least one file descriptor has retired.
|
||||||
|
bool retired;
|
||||||
|
|
||||||
|
// List of all the engines waiting for the timer event.
|
||||||
|
typedef std::vector <struct i_poll_events*> timers_t;
|
||||||
|
timers_t timers;
|
||||||
|
|
||||||
|
// If true, thread is shutting down.
|
||||||
|
bool stopping;
|
||||||
|
|
||||||
|
// Handle of the physical thread doing the I/O work.
|
||||||
|
thread_t worker;
|
||||||
|
|
||||||
|
// Load of the poller. Currently number of file descriptors
|
||||||
|
// registered with the poller.
|
||||||
|
atomic_counter_t load;
|
||||||
|
|
||||||
|
select_t (const select_t&);
|
||||||
|
void operator = (const select_t&);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
273
src/session.cpp
Normal file
273
src/session.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
107
src/session.hpp
Normal file
107
src/session.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
110
src/session_stub.cpp
Normal file
110
src/session_stub.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
83
src/session_stub.hpp
Normal file
83
src/session_stub.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_SESSION_STUB_HPP_INCLUDED__
|
||||||
|
#define __ZS_SESSION_STUB_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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
|
188
src/simple_semaphore.hpp
Normal file
188
src/simple_semaphore.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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 <pthread.h>
|
||||||
|
#elif defined ZS_HAVE_WINDOWS
|
||||||
|
#include "windows.hpp"
|
||||||
|
#else
|
||||||
|
#include <semaphore.h>
|
||||||
|
#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
|
267
src/socket_base.cpp
Normal file
267
src/socket_base.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#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.
|
||||||
|
}
|
||||||
|
|
96
src/socket_base.hpp
Normal file
96
src/socket_base.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_SOCKET_BASE_HPP_INCLUDED__
|
||||||
|
#define __ZS_SOCKET_BASE_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#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 <class io_object_t*> 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
|
70
src/stdint.hpp
Normal file
70
src/stdint.hpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ZS_STDINT_HPP_INCLUDED__
|
||||||
|
#define __ZS_STDINT_HPP_INCLUDED__
|
||||||
|
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
|
#ifdef ZS_HAVE_SOLARIS
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#elif defined _MSC_VER
|
||||||
|
|
||||||
|
#ifndef int8_t
|
||||||
|
typedef __int8 int8_t;
|
||||||
|
#endif
|
||||||
|
#ifndef int16_t
|
||||||
|
typedef __int16 int16_t;
|
||||||
|
#endif
|
||||||
|
#ifndef int32_t
|
||||||
|
typedef __int32 int32_t;
|
||||||
|
#endif
|
||||||
|
#ifndef int64_t
|
||||||
|
typedef __int64 int64_t;
|
||||||
|
#endif
|
||||||
|
#ifndef uint8_t
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
#endif
|
||||||
|
#ifndef uint16_t
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
#endif
|
||||||
|
#ifndef uint32_t
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
#endif
|
||||||
|
#ifndef uint64_t
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined ZMQ_HAVE_OPENVMS
|
||||||
|
|
||||||
|
#include <types.h>
|
||||||
|
typedef unsigned __int8 uint8_t;
|
||||||
|
typedef unsigned __int16 uint16_t;
|
||||||
|
typedef unsigned __int32 uint32_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user