Commit 05d90849 authored by Martin Sustrik's avatar Martin Sustrik

WIP: Socket migration between threads, new zmq_close() semantics

Sockets may now be migrated between OS threads; sockets may not be used by
more than one thread at any time. To migrate a socket to another thread the
caller must ensure that a full memory barrier is called before using the
socket from the target thread.

The new zmq_close() semantics implement the behaviour discussed at:

http://lists.zeromq.org/pipermail/zeromq-dev/2010-July/004244.html

Specifically, zmq_close() is now deterministic and while it still returns
immediately, it does not discard any data that may still be queued for
sending. Further, zmq_term() will now block until all outstanding data has
been sent.

TODO: Many bugs have been introduced, needs testing. Further, SO_LINGER or
an equivalent mechanism (possibly a configurable timeout to zmq_term())
needs to be implemented.
parent b7e0fa97
...@@ -82,7 +82,6 @@ ZMQ_EXPORT void zmq_version (int *major, int *minor, int *patch); ...@@ -82,7 +82,6 @@ ZMQ_EXPORT void zmq_version (int *major, int *minor, int *patch);
#endif #endif
/* Native 0MQ error codes. */ /* Native 0MQ error codes. */
#define EMTHREAD (ZMQ_HAUSNUMERO + 50)
#define EFSM (ZMQ_HAUSNUMERO + 51) #define EFSM (ZMQ_HAUSNUMERO + 51)
#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52) #define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52)
#define ETERM (ZMQ_HAUSNUMERO + 53) #define ETERM (ZMQ_HAUSNUMERO + 53)
...@@ -161,12 +160,13 @@ ZMQ_EXPORT int zmq_term (void *context); ...@@ -161,12 +160,13 @@ ZMQ_EXPORT int zmq_term (void *context);
#define ZMQ_XREP 6 #define ZMQ_XREP 6
#define ZMQ_PULL 7 #define ZMQ_PULL 7
#define ZMQ_PUSH 8 #define ZMQ_PUSH 8
#define ZMQ_UPSTREAM ZMQ_PULL /* Old alias, remove in 3.x */
#define ZMQ_DOWNSTREAM ZMQ_PUSH /* Old alias, remove in 3.x */ /* Deprecated aliases, to be removed in release 3.x */
#define ZMQ_UPSTREAM ZMQ_PULL
#define ZMQ_DOWNSTREAM ZMQ_PUSH
/* Socket options. */ /* Socket options. */
#define ZMQ_HWM 1 #define ZMQ_HWM 1
/* ZMQ_LWM 2 no longer supported */
#define ZMQ_SWAP 3 #define ZMQ_SWAP 3
#define ZMQ_AFFINITY 4 #define ZMQ_AFFINITY 4
#define ZMQ_IDENTITY 5 #define ZMQ_IDENTITY 5
...@@ -178,6 +178,8 @@ ZMQ_EXPORT int zmq_term (void *context); ...@@ -178,6 +178,8 @@ ZMQ_EXPORT int zmq_term (void *context);
#define ZMQ_SNDBUF 11 #define ZMQ_SNDBUF 11
#define ZMQ_RCVBUF 12 #define ZMQ_RCVBUF 12
#define ZMQ_RCVMORE 13 #define ZMQ_RCVMORE 13
#define ZMQ_FD 14
#define ZMQ_EVENTS 15
/* Send/recv options. */ /* Send/recv options. */
#define ZMQ_NOBLOCK 1 #define ZMQ_NOBLOCK 1
...@@ -217,17 +219,15 @@ typedef struct ...@@ -217,17 +219,15 @@ typedef struct
ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout); ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout);
/******************************************************************************/ /******************************************************************************/
/* Devices */ /* Devices - Experimental. */
/******************************************************************************/ /******************************************************************************/
#define ZMQ_QUEUE 1 #define ZMQ_STREAMER 1
#define ZMQ_FORWARDER 2 #define ZMQ_FORWARDER 2
#define ZMQ_STREAMER 3 #define ZMQ_QUEUE 3
ZMQ_EXPORT int zmq_device (int device, void * insocket, void* outsocket); ZMQ_EXPORT int zmq_device (int device, void * insocket, void* outsocket);
#undef ZMQ_EXPORT
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
......
...@@ -49,7 +49,7 @@ endif ...@@ -49,7 +49,7 @@ endif
nodist_libzmq_la_SOURCES = $(pgm_sources) nodist_libzmq_la_SOURCES = $(pgm_sources)
libzmq_la_SOURCES = app_thread.hpp \ libzmq_la_SOURCES = \
atomic_counter.hpp \ atomic_counter.hpp \
atomic_ptr.hpp \ atomic_ptr.hpp \
blob.hpp \ blob.hpp \
...@@ -58,7 +58,6 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -58,7 +58,6 @@ libzmq_la_SOURCES = app_thread.hpp \
ctx.hpp \ ctx.hpp \
decoder.hpp \ decoder.hpp \
devpoll.hpp \ devpoll.hpp \
push.hpp \
encoder.hpp \ encoder.hpp \
epoll.hpp \ epoll.hpp \
err.hpp \ err.hpp \
...@@ -69,7 +68,6 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -69,7 +68,6 @@ libzmq_la_SOURCES = app_thread.hpp \
io_object.hpp \ io_object.hpp \
io_thread.hpp \ io_thread.hpp \
ip.hpp \ ip.hpp \
i_endpoint.hpp \
i_engine.hpp \ i_engine.hpp \
i_poll_events.hpp \ i_poll_events.hpp \
kqueue.hpp \ kqueue.hpp \
...@@ -91,10 +89,13 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -91,10 +89,13 @@ libzmq_la_SOURCES = app_thread.hpp \
pair.hpp \ pair.hpp \
prefix_tree.hpp \ prefix_tree.hpp \
pub.hpp \ pub.hpp \
pull.hpp \
push.hpp \
queue.hpp \ queue.hpp \
rep.hpp \ rep.hpp \
req.hpp \ req.hpp \
select.hpp \ select.hpp \
semaphore.hpp \
session.hpp \ session.hpp \
signaler.hpp \ signaler.hpp \
socket_base.hpp \ socket_base.hpp \
...@@ -105,7 +106,6 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -105,7 +106,6 @@ libzmq_la_SOURCES = app_thread.hpp \
tcp_listener.hpp \ tcp_listener.hpp \
tcp_socket.hpp \ tcp_socket.hpp \
thread.hpp \ thread.hpp \
pull.hpp \
uuid.hpp \ uuid.hpp \
windows.hpp \ windows.hpp \
wire.hpp \ wire.hpp \
...@@ -121,11 +121,9 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -121,11 +121,9 @@ libzmq_la_SOURCES = app_thread.hpp \
zmq_engine.hpp \ zmq_engine.hpp \
zmq_init.hpp \ zmq_init.hpp \
zmq_listener.hpp \ zmq_listener.hpp \
app_thread.cpp \
command.cpp \ command.cpp \
ctx.cpp \ ctx.cpp \
devpoll.cpp \ devpoll.cpp \
push.cpp \
epoll.cpp \ epoll.cpp \
err.cpp \ err.cpp \
forwarder.cpp \ forwarder.cpp \
...@@ -139,13 +137,15 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -139,13 +137,15 @@ libzmq_la_SOURCES = app_thread.hpp \
object.cpp \ object.cpp \
options.cpp \ options.cpp \
owned.cpp \ owned.cpp \
pair.cpp \
pgm_receiver.cpp \ pgm_receiver.cpp \
pgm_sender.cpp \ pgm_sender.cpp \
pgm_socket.cpp \ pgm_socket.cpp \
pair.cpp \
prefix_tree.cpp \ prefix_tree.cpp \
pipe.cpp \ pipe.cpp \
poll.cpp \ poll.cpp \
pull.cpp \
push.cpp \
pub.cpp \ pub.cpp \
queue.cpp \ queue.cpp \
rep.cpp \ rep.cpp \
...@@ -160,7 +160,6 @@ libzmq_la_SOURCES = app_thread.hpp \ ...@@ -160,7 +160,6 @@ libzmq_la_SOURCES = app_thread.hpp \
tcp_listener.cpp \ tcp_listener.cpp \
tcp_socket.cpp \ tcp_socket.cpp \
thread.cpp \ thread.cpp \
pull.cpp \
uuid.cpp \ uuid.cpp \
xrep.cpp \ xrep.cpp \
xreq.cpp \ xreq.cpp \
......
/*
Copyright (c) 2007-2010 iMatix Corporation
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the Lesser GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <new>
#include <algorithm>
#include "../include/zmq.h"
#include "platform.hpp"
#if defined ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#if defined _MSC_VER
#include <intrin.h>
#endif
#else
#include <unistd.h>
#endif
#include "app_thread.hpp"
#include "ctx.hpp"
#include "err.hpp"
#include "pipe.hpp"
#include "config.hpp"
#include "socket_base.hpp"
#include "pair.hpp"
#include "pub.hpp"
#include "sub.hpp"
#include "req.hpp"
#include "rep.hpp"
#include "xreq.hpp"
#include "xrep.hpp"
#include "pull.hpp"
#include "push.hpp"
// If the RDTSC is available we use it to prevent excessive
// polling for commands. The nice thing here is that it will work on any
// system with x86 architecture and gcc or MSVC compiler.
#if (defined __GNUC__ && (defined __i386__ || defined __x86_64__)) ||\
(defined _MSC_VER && (defined _M_IX86 || defined _M_X64))
#define ZMQ_DELAY_COMMANDS
#endif
zmq::app_thread_t::app_thread_t (ctx_t *ctx_,
uint32_t thread_slot_) :
object_t (ctx_, thread_slot_),
last_processing_time (0),
terminated (false)
{
}
zmq::app_thread_t::~app_thread_t ()
{
zmq_assert (sockets.empty ());
}
void zmq::app_thread_t::stop ()
{
send_stop ();
}
zmq::signaler_t *zmq::app_thread_t::get_signaler ()
{
return &signaler;
}
bool zmq::app_thread_t::process_commands (bool block_, bool throttle_)
{
bool received;
command_t cmd;
if (block_) {
received = signaler.recv (&cmd, true);
zmq_assert (received);
}
else {
#if defined ZMQ_DELAY_COMMANDS
// Optimised version of command processing - it doesn't have to check
// for incoming commands each time. It does so only if certain time
// elapsed since last command processing. Command delay varies
// depending on CPU speed: It's ~1ms on 3GHz CPU, ~2ms on 1.5GHz CPU
// etc. The optimisation makes sense only on platforms where getting
// a timestamp is a very cheap operation (tens of nanoseconds).
if (throttle_) {
// Get timestamp counter.
#if defined __GNUC__
uint32_t low;
uint32_t high;
__asm__ volatile ("rdtsc" : "=a" (low), "=d" (high));
uint64_t current_time = (uint64_t) high << 32 | low;
#elif defined _MSC_VER
uint64_t current_time = __rdtsc ();
#else
#error
#endif
// Check whether certain time have elapsed since last command
// processing.
if (current_time - last_processing_time <= max_command_delay)
return !terminated;
last_processing_time = current_time;
}
#endif
// Check whether there are any commands pending for this thread.
received = signaler.recv (&cmd, false);
}
// Process all the commands available at the moment.
while (received) {
cmd.destination->process_command (cmd);
received = signaler.recv (&cmd, false);
}
return !terminated;
}
zmq::socket_base_t *zmq::app_thread_t::create_socket (int type_)
{
socket_base_t *s = NULL;
switch (type_) {
case ZMQ_PAIR:
s = new (std::nothrow) pair_t (this);
break;
case ZMQ_PUB:
s = new (std::nothrow) pub_t (this);
break;
case ZMQ_SUB:
s = new (std::nothrow) sub_t (this);
break;
case ZMQ_REQ:
s = new (std::nothrow) req_t (this);
break;
case ZMQ_REP:
s = new (std::nothrow) rep_t (this);
break;
case ZMQ_XREQ:
s = new (std::nothrow) xreq_t (this);
break;
case ZMQ_XREP:
s = new (std::nothrow) xrep_t (this);
break;
case ZMQ_PULL:
s = new (std::nothrow) pull_t (this);
break;
case ZMQ_PUSH:
s = new (std::nothrow) push_t (this);
break;
default:
if (sockets.empty ())
get_ctx ()->no_sockets (this);
errno = EINVAL;
return NULL;
}
zmq_assert (s);
sockets.push_back (s);
return s;
}
void zmq::app_thread_t::remove_socket (socket_base_t *socket_)
{
sockets.erase (socket_);
if (sockets.empty ())
get_ctx ()->no_sockets (this);
}
void zmq::app_thread_t::process_stop ()
{
terminated = true;
}
bool zmq::app_thread_t::is_terminated ()
{
return terminated;
}
/*
Copyright (c) 2007-2010 iMatix Corporation
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the Lesser GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_APP_THREAD_HPP_INCLUDED__
#define __ZMQ_APP_THREAD_HPP_INCLUDED__
#include <vector>
#include "stdint.hpp"
#include "object.hpp"
#include "yarray.hpp"
#include "signaler.hpp"
namespace zmq
{
class app_thread_t : public object_t
{
public:
app_thread_t (class ctx_t *ctx_, uint32_t thread_slot_);
~app_thread_t ();
// Interrupt blocking call if the app thread is stuck in one.
// This function is is called from a different thread!
void stop ();
// Returns signaler associated with this application thread.
signaler_t *get_signaler ();
// Processes commands sent to this thread (if any). If 'block' is
// set to true, returns only after at least one command was processed.
// If throttle argument is true, commands are processed at most once
// in a predefined time period. The function returns false is the
// associated context was terminated, true otherwise.
bool process_commands (bool block_, bool throttle_);
// Create a socket of a specified type.
class socket_base_t *create_socket (int type_);
// Unregister the socket from the app_thread (called by socket itself).
void remove_socket (class socket_base_t *socket_);
// Returns true is the associated context was already terminated.
bool is_terminated ();
private:
// Command handlers.
void process_stop ();
// All the sockets created from this application thread.
typedef yarray_t <socket_base_t> sockets_t;
sockets_t sockets;
// App thread's signaler object.
signaler_t signaler;
// Timestamp of when commands were processed the last time.
uint64_t last_processing_time;
// If true, 'stop' command was already received.
bool terminated;
app_thread_t (const app_thread_t&);
void operator = (const app_thread_t&);
};
}
#endif
...@@ -27,9 +27,8 @@ namespace zmq ...@@ -27,9 +27,8 @@ namespace zmq
enum enum
{ {
// Maximal number of OS threads that can own 0MQ sockets // Maximum number of sockets that can be opened at the same time.
// at the same time. max_sockets = 512,
max_app_threads = 512,
// Number of new messages in message pipe needed to trigger new memory // Number of new messages in message pipe needed to trigger new memory
// allocation. Setting this parameter to 256 decreases the impact of // allocation. Setting this parameter to 256 decreases the impact of
......
This diff is collapsed.
...@@ -26,7 +26,9 @@ ...@@ -26,7 +26,9 @@
#include <string> #include <string>
#include "signaler.hpp" #include "signaler.hpp"
#include "semaphore.hpp"
#include "ypipe.hpp" #include "ypipe.hpp"
#include "yarray.hpp"
#include "config.hpp" #include "config.hpp"
#include "mutex.hpp" #include "mutex.hpp"
#include "stdint.hpp" #include "stdint.hpp"
...@@ -55,29 +57,19 @@ namespace zmq ...@@ -55,29 +57,19 @@ namespace zmq
// Create a socket. // Create a socket.
class socket_base_t *create_socket (int type_); class socket_base_t *create_socket (int type_);
// Destroy a socket. // Make socket a zombie.
void destroy_socket (); void zombify (socket_base_t *socket_);
// Called by app_thread_t when it has no more sockets. The function // Send command to the destination slot.
// should disassociate the object from the current OS thread. void send_command (uint32_t slot_, const command_t &command_);
void no_sockets (class app_thread_t *thread_);
// Send command to the destination thread. // Receive command from the source slot.
void send_command (uint32_t destination_, const command_t &command_); bool recv_command (uint32_t slot_, command_t *command_, bool block_);
// Receive command from another thread.
bool recv_command (uint32_t thread_slot_, command_t *command_,
bool block_);
// Returns the I/O thread that is the least busy at the moment. // Returns the I/O thread that is the least busy at the moment.
// Taskset specifies which I/O threads are eligible (0 = all). // Taskset specifies which I/O threads are eligible (0 = all).
class io_thread_t *choose_io_thread (uint64_t taskset_); class io_thread_t *choose_io_thread (uint64_t taskset_);
// All pipes are registered with the context so that even the
// orphaned pipes can be deallocated on the terminal shutdown.
void register_pipe (class pipe_t *pipe_);
void unregister_pipe (class pipe_t *pipe_);
// Management of inproc endpoints. // Management of inproc endpoints.
int register_endpoint (const char *addr_, class socket_base_t *socket_); int register_endpoint (const char *addr_, class socket_base_t *socket_);
void unregister_endpoints (class socket_base_t *socket_); void unregister_endpoints (class socket_base_t *socket_);
...@@ -87,57 +79,45 @@ namespace zmq ...@@ -87,57 +79,45 @@ namespace zmq
~ctx_t (); ~ctx_t ();
struct app_thread_info_t // Sockets belonging to this context.
{ typedef yarray_t <socket_base_t> sockets_t;
// If false, 0MQ application thread is free, there's no associated sockets_t sockets;
// OS thread.
bool associated;
// ID of the associated OS thread. If 'associated' is false, // Array of sockets that were already closed but not yet deallocated.
// this field contains bogus data. // These sockets still have some pipes and I/O objects attached.
thread_t::id_t tid; typedef yarray_t <socket_base_t> zombies_t;
zombies_t zombies;
// Pointer to the 0MQ application thread object. // List of unused slots.
class app_thread_t *app_thread; typedef std::vector <uint32_t> emtpy_slots_t;
}; emtpy_slots_t empty_slots;
// If true, shutdown thread wants to be informed when there are no
// more open sockets. Do so by posting no_sockets_sync semaphore.
// Note that this variable is synchronised by slot_sync mutex.
bool no_sockets_notify;
// Object used by zmq_term to wait while all the sockets are closed
// by different application threads.
semaphore_t no_sockets_sync;
// Application threads. // Synchronisation of accesses to global slot-related data:
typedef std::vector <app_thread_info_t> app_threads_t; // sockets, zombies, empty_slots, terminated. It also synchronises
app_threads_t app_threads; // access to zombie sockets as such (as oposed to slots) and provides
// a memory barrier to ensure that all CPU cores see the same data.
mutex_t slot_sync;
// Synchronisation of accesses to shared application thread data. // This function attempts to deallocate as many zombie sockets as
mutex_t app_threads_sync; // possible. It must be called within a slot_sync critical section.
void dezombify ();
// I/O threads. // I/O threads.
typedef std::vector <class io_thread_t*> io_threads_t; typedef std::vector <class io_thread_t*> io_threads_t;
io_threads_t io_threads; io_threads_t io_threads;
// Array of pointers to signalers for both application and I/O threads. // Array of pointers to signalers for both application and I/O threads.
int signalers_count; uint32_t slot_count;
signaler_t **signalers; signaler_t **slots;
// As pipes may reside in orphaned state in particular moments
// of the pipe shutdown process, i.e. neither pipe reader nor
// pipe writer hold reference to the pipe, we have to hold references
// to all pipes in context so that we can deallocate them
// during terminal shutdown even though it conincides with the
// pipe being in the orphaned state.
typedef std::set <class pipe_t*> pipes_t;
pipes_t pipes;
// Synchronisation of access to the pipes repository.
mutex_t pipes_sync;
// Number of sockets alive.
int sockets;
// If true, zmq_term was already called. When last socket is closed
// the whole 0MQ infrastructure should be deallocated.
bool terminated;
// Synchronisation of access to the termination data (socket count
// and 'terminated' flag).
mutex_t term_sync;
// List of inproc endpoints within this context. // List of inproc endpoints within this context.
typedef std::map <std::string, class socket_base_t*> endpoints_t; typedef std::map <std::string, class socket_base_t*> endpoints_t;
......
...@@ -32,18 +32,19 @@ zmq::fq_t::fq_t () : ...@@ -32,18 +32,19 @@ zmq::fq_t::fq_t () :
zmq::fq_t::~fq_t () zmq::fq_t::~fq_t ()
{ {
for (pipes_t::size_type i = 0; i != pipes.size (); i++) zmq_assert (pipes.empty ());
pipes [i]->term ();
} }
void zmq::fq_t::attach (reader_t *pipe_) void zmq::fq_t::attach (reader_t *pipe_)
{ {
pipe_->set_event_sink (this);
pipes.push_back (pipe_); pipes.push_back (pipe_);
pipes.swap (active, pipes.size () - 1); pipes.swap (active, pipes.size () - 1);
active++; active++;
} }
void zmq::fq_t::detach (reader_t *pipe_) void zmq::fq_t::terminated (reader_t *pipe_)
{ {
zmq_assert (!more || pipes [current] != pipe_); zmq_assert (!more || pipes [current] != pipe_);
...@@ -57,16 +58,18 @@ void zmq::fq_t::detach (reader_t *pipe_) ...@@ -57,16 +58,18 @@ void zmq::fq_t::detach (reader_t *pipe_)
pipes.erase (pipe_); pipes.erase (pipe_);
} }
void zmq::fq_t::kill (reader_t *pipe_) bool zmq::fq_t::has_pipes ()
{ {
// Move the pipe to the list of inactive pipes. return !pipes.empty ();
active--; }
if (current == active)
current = 0; void zmq::fq_t::term_pipes ()
pipes.swap (pipes.index (pipe_), active); {
for (pipes_t::size_type i = 0; i != pipes.size (); i++)
pipes [i]->terminate ();
} }
void zmq::fq_t::revive (reader_t *pipe_) void zmq::fq_t::activated (reader_t *pipe_)
{ {
// Move the pipe to the list of active pipes. // Move the pipe to the list of active pipes.
pipes.swap (pipes.index (pipe_), active); pipes.swap (pipes.index (pipe_), active);
...@@ -98,6 +101,12 @@ int zmq::fq_t::recv (zmq_msg_t *msg_, int flags_) ...@@ -98,6 +101,12 @@ int zmq::fq_t::recv (zmq_msg_t *msg_, int flags_)
} }
return 0; return 0;
} }
else {
active--;
pipes.swap (current, active);
if (current == active)
current = 0;
}
} }
// No message is available. Initialise the output parameter // No message is available. Initialise the output parameter
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define __ZMQ_FQ_HPP_INCLUDED__ #define __ZMQ_FQ_HPP_INCLUDED__
#include "yarray.hpp" #include "yarray.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
...@@ -28,24 +29,28 @@ namespace zmq ...@@ -28,24 +29,28 @@ namespace zmq
// Class manages a set of inbound pipes. On receive it performs fair // Class manages a set of inbound pipes. On receive it performs fair
// queueing (RFC970) so that senders gone berserk won't cause denial of // queueing (RFC970) so that senders gone berserk won't cause denial of
// service for decent senders. // service for decent senders.
class fq_t class fq_t : public i_reader_events
{ {
public: public:
fq_t (); fq_t ();
~fq_t (); ~fq_t ();
void attach (class reader_t *pipe_); void attach (reader_t *pipe_);
void detach (class reader_t *pipe_); bool has_pipes ();
void kill (class reader_t *pipe_); void term_pipes ();
void revive (class reader_t *pipe_);
int recv (zmq_msg_t *msg_, int flags_); int recv (zmq_msg_t *msg_, int flags_);
bool has_in (); bool has_in ();
// i_reader_events implementation.
void activated (reader_t *pipe_);
void terminated (reader_t *pipe_);
private: private:
// Inbound pipes. // Inbound pipes.
typedef yarray_t <class reader_t> pipes_t; typedef yarray_t <reader_t> pipes_t;
pipes_t pipes; pipes_t pipes;
// Number of active pipes. All the active pipes are located at the // Number of active pipes. All the active pipes are located at the
......
/*
Copyright (c) 2007-2010 iMatix Corporation
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the Lesser GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_I_ENDPOINT_HPP_INCLUDED__
#define __ZMQ_I_ENDPOINT_HPP_INCLUDED__
#include "blob.hpp"
namespace zmq
{
struct i_endpoint
{
virtual ~i_endpoint () {}
virtual void attach_pipes (class reader_t *inpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) = 0;
virtual void detach_inpipe (class reader_t *pipe_) = 0;
virtual void detach_outpipe (class writer_t *pipe_) = 0;
virtual void kill (class reader_t *pipe_) = 0;
virtual void revive (class reader_t *pipe_) = 0;
virtual void revive (class writer_t *pipe_) = 0;
};
}
#endif
...@@ -26,9 +26,8 @@ ...@@ -26,9 +26,8 @@
#include "err.hpp" #include "err.hpp"
#include "ctx.hpp" #include "ctx.hpp"
zmq::io_thread_t::io_thread_t (ctx_t *ctx_, zmq::io_thread_t::io_thread_t (ctx_t *ctx_, uint32_t slot_) :
uint32_t thread_slot_) : object_t (ctx_, slot_)
object_t (ctx_, thread_slot_)
{ {
poller = new (std::nothrow) poller_t; poller = new (std::nothrow) poller_t;
zmq_assert (poller); zmq_assert (poller);
......
...@@ -38,7 +38,7 @@ namespace zmq ...@@ -38,7 +38,7 @@ namespace zmq
{ {
public: public:
io_thread_t (class ctx_t *ctx_, uint32_t thread_slot_); io_thread_t (class ctx_t *ctx_, uint32_t slot_);
// Clean-up. If the thread was started, it's neccessary to call 'stop' // Clean-up. If the thread was started, it's neccessary to call 'stop'
// before invoking destructor. Otherwise the destructor would hang up. // before invoking destructor. Otherwise the destructor would hang up.
......
...@@ -32,19 +32,27 @@ zmq::lb_t::lb_t () : ...@@ -32,19 +32,27 @@ zmq::lb_t::lb_t () :
zmq::lb_t::~lb_t () zmq::lb_t::~lb_t ()
{ {
for (pipes_t::size_type i = 0; i != pipes.size (); i++) zmq_assert (pipes.empty ());
pipes [i]->term ();
} }
void zmq::lb_t::attach (writer_t *pipe_) void zmq::lb_t::attach (writer_t *pipe_)
{ {
pipe_->set_event_sink (this);
pipes.push_back (pipe_); pipes.push_back (pipe_);
pipes.swap (active, pipes.size () - 1); pipes.swap (active, pipes.size () - 1);
active++; active++;
} }
void zmq::lb_t::detach (writer_t *pipe_) void zmq::lb_t::term_pipes ()
{ {
for (pipes_t::size_type i = 0; i != pipes.size (); i++)
pipes [i]->terminate ();
}
void zmq::lb_t::terminated (writer_t *pipe_)
{
// ???
zmq_assert (!more || pipes [current] != pipe_); zmq_assert (!more || pipes [current] != pipe_);
// Remove the pipe from the list; adjust number of active pipes // Remove the pipe from the list; adjust number of active pipes
...@@ -57,7 +65,12 @@ void zmq::lb_t::detach (writer_t *pipe_) ...@@ -57,7 +65,12 @@ void zmq::lb_t::detach (writer_t *pipe_)
pipes.erase (pipe_); pipes.erase (pipe_);
} }
void zmq::lb_t::revive (writer_t *pipe_) bool zmq::lb_t::has_pipes ()
{
return !pipes.empty ();
}
void zmq::lb_t::activated (writer_t *pipe_)
{ {
// Move the pipe to the list of active pipes. // Move the pipe to the list of active pipes.
pipes.swap (pipes.index (pipe_), active); pipes.swap (pipes.index (pipe_), active);
......
...@@ -21,25 +21,30 @@ ...@@ -21,25 +21,30 @@
#define __ZMQ_LB_HPP_INCLUDED__ #define __ZMQ_LB_HPP_INCLUDED__
#include "yarray.hpp" #include "yarray.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
// Class manages a set of outbound pipes. On send it load balances // Class manages a set of outbound pipes. On send it load balances
// messages fairly among the pipes. // messages fairly among the pipes.
class lb_t class lb_t : public i_writer_events
{ {
public: public:
lb_t (); lb_t ();
~lb_t (); ~lb_t ();
void attach (class writer_t *pipe_); void attach (writer_t *pipe_);
void detach (class writer_t *pipe_); void term_pipes ();
void revive (class writer_t *pipe_); bool has_pipes ();
int send (zmq_msg_t *msg_, int flags_); int send (zmq_msg_t *msg_, int flags_);
bool has_out (); bool has_out ();
// i_writer_events interface implementation.
void activated (writer_t *pipe_);
void terminated (writer_t *pipe_);
private: private:
// List of outbound pipes. // List of outbound pipes.
......
...@@ -28,15 +28,15 @@ ...@@ -28,15 +28,15 @@
#include "session.hpp" #include "session.hpp"
#include "socket_base.hpp" #include "socket_base.hpp"
zmq::object_t::object_t (ctx_t *ctx_, uint32_t thread_slot_) : zmq::object_t::object_t (ctx_t *ctx_, uint32_t slot_) :
ctx (ctx_), ctx (ctx_),
thread_slot (thread_slot_) slot (slot_)
{ {
} }
zmq::object_t::object_t (object_t *parent_) : zmq::object_t::object_t (object_t *parent_) :
ctx (parent_->ctx), ctx (parent_->ctx),
thread_slot (parent_->thread_slot) slot (parent_->slot)
{ {
} }
...@@ -44,9 +44,9 @@ zmq::object_t::~object_t () ...@@ -44,9 +44,9 @@ zmq::object_t::~object_t ()
{ {
} }
uint32_t zmq::object_t::get_thread_slot () uint32_t zmq::object_t::get_slot ()
{ {
return thread_slot; return slot;
} }
zmq::ctx_t *zmq::object_t::get_ctx () zmq::ctx_t *zmq::object_t::get_ctx ()
...@@ -123,16 +123,6 @@ void zmq::object_t::process_command (command_t &cmd_) ...@@ -123,16 +123,6 @@ void zmq::object_t::process_command (command_t &cmd_)
deallocate_command (&cmd_); deallocate_command (&cmd_);
} }
void zmq::object_t::register_pipe (class pipe_t *pipe_)
{
ctx->register_pipe (pipe_);
}
void zmq::object_t::unregister_pipe (class pipe_t *pipe_)
{
ctx->unregister_pipe (pipe_);
}
int zmq::object_t::register_endpoint (const char *addr_, socket_base_t *socket_) int zmq::object_t::register_endpoint (const char *addr_, socket_base_t *socket_)
{ {
return ctx->register_endpoint (addr_, socket_); return ctx->register_endpoint (addr_, socket_);
...@@ -153,6 +143,11 @@ zmq::io_thread_t *zmq::object_t::choose_io_thread (uint64_t taskset_) ...@@ -153,6 +143,11 @@ zmq::io_thread_t *zmq::object_t::choose_io_thread (uint64_t taskset_)
return ctx->choose_io_thread (taskset_); return ctx->choose_io_thread (taskset_);
} }
void zmq::object_t::zombify (socket_base_t *socket_)
{
ctx->zombify (socket_);
}
void zmq::object_t::send_stop () void zmq::object_t::send_stop ()
{ {
// 'stop' command goes always from administrative thread to // 'stop' command goes always from administrative thread to
...@@ -160,7 +155,7 @@ void zmq::object_t::send_stop () ...@@ -160,7 +155,7 @@ void zmq::object_t::send_stop ()
command_t cmd; command_t cmd;
cmd.destination = this; cmd.destination = this;
cmd.type = command_t::stop; cmd.type = command_t::stop;
ctx->send_command (thread_slot, cmd); ctx->send_command (slot, cmd);
} }
void zmq::object_t::send_plug (owned_t *destination_, bool inc_seqnum_) void zmq::object_t::send_plug (owned_t *destination_, bool inc_seqnum_)
...@@ -369,6 +364,6 @@ void zmq::object_t::process_seqnum () ...@@ -369,6 +364,6 @@ void zmq::object_t::process_seqnum ()
void zmq::object_t::send_command (command_t &cmd_) void zmq::object_t::send_command (command_t &cmd_)
{ {
ctx->send_command (cmd_.destination->get_thread_slot (), cmd_); ctx->send_command (cmd_.destination->get_slot (), cmd_);
} }
...@@ -32,18 +32,14 @@ namespace zmq ...@@ -32,18 +32,14 @@ namespace zmq
{ {
public: public:
object_t (class ctx_t *ctx_, uint32_t thread_slot_); object_t (class ctx_t *ctx_, uint32_t slot_);
object_t (object_t *parent_); object_t (object_t *parent_);
virtual ~object_t (); virtual ~object_t ();
uint32_t get_thread_slot (); uint32_t get_slot ();
ctx_t *get_ctx (); ctx_t *get_ctx ();
void process_command (struct command_t &cmd_); void process_command (struct command_t &cmd_);
// Allow pipe to access corresponding context functions.
void register_pipe (class pipe_t *pipe_);
void unregister_pipe (class pipe_t *pipe_);
protected: protected:
// Using following function, socket is able to access global // Using following function, socket is able to access global
...@@ -55,6 +51,10 @@ namespace zmq ...@@ -55,6 +51,10 @@ namespace zmq
// Chooses least loaded I/O thread. // Chooses least loaded I/O thread.
class io_thread_t *choose_io_thread (uint64_t taskset_); class io_thread_t *choose_io_thread (uint64_t taskset_);
// Zombify particular socket. In other words, pass the ownership to
// the context.
void zombify (class socket_base_t *socket_);
// Derived object can use these functions to send commands // Derived object can use these functions to send commands
// to other objects. // to other objects.
void send_stop (); void send_stop ();
...@@ -105,7 +105,7 @@ namespace zmq ...@@ -105,7 +105,7 @@ namespace zmq
class ctx_t *ctx; class ctx_t *ctx;
// Slot ID of the thread the object belongs to. // Slot ID of the thread the object belongs to.
uint32_t thread_slot; uint32_t slot;
void send_command (command_t &cmd_); void send_command (command_t &cmd_);
......
...@@ -35,7 +35,7 @@ zmq::owned_t::~owned_t () ...@@ -35,7 +35,7 @@ zmq::owned_t::~owned_t ()
void zmq::owned_t::inc_seqnum () void zmq::owned_t::inc_seqnum ()
{ {
// NB: This function may be called from a different thread! // This function may be called from a different thread!
sent_seqnum.add (1); sent_seqnum.add (1);
} }
...@@ -62,10 +62,16 @@ void zmq::owned_t::finalise () ...@@ -62,10 +62,16 @@ void zmq::owned_t::finalise ()
{ {
// If termination request was already received and there are no more // If termination request was already received and there are no more
// commands to wait for, terminate the object. // commands to wait for, terminate the object.
if (shutting_down && processed_seqnum == sent_seqnum.get ()) { if (shutting_down && processed_seqnum == sent_seqnum.get ()
&& is_terminable ()) {
process_unplug (); process_unplug ();
send_term_ack (owner); send_term_ack (owner);
delete this; delete this;
} }
} }
bool zmq::owned_t::is_terminable ()
{
return true;
}
...@@ -45,6 +45,13 @@ namespace zmq ...@@ -45,6 +45,13 @@ namespace zmq
protected: protected:
// A mechanism allowing derived owned objects to postpone the
// termination process. Default implementation defines no such delay.
// Note that the derived object has to call finalise method when the
// delay is over.
virtual bool is_terminable ();
void finalise ();
// Ask owner socket to terminate this object. // Ask owner socket to terminate this object.
void term (); void term ();
...@@ -69,8 +76,6 @@ namespace zmq ...@@ -69,8 +76,6 @@ namespace zmq
void process_term (); void process_term ();
void process_seqnum (); void process_seqnum ();
void finalise ();
// Sequence number of the last command sent to this object. // Sequence number of the last command sent to this object.
atomic_counter_t sent_seqnum; atomic_counter_t sent_seqnum;
......
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
#include "err.hpp" #include "err.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::pair_t::pair_t (class app_thread_t *parent_) : zmq::pair_t::pair_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
inpipe (NULL), inpipe (NULL),
outpipe (NULL), outpipe (NULL),
alive (true) inpipe_alive (false),
outpipe_alive (false)
{ {
options.requires_in = true; options.requires_in = true;
options.requires_out = true; options.requires_out = true;
...@@ -35,56 +36,61 @@ zmq::pair_t::pair_t (class app_thread_t *parent_) : ...@@ -35,56 +36,61 @@ zmq::pair_t::pair_t (class app_thread_t *parent_) :
zmq::pair_t::~pair_t () zmq::pair_t::~pair_t ()
{ {
if (inpipe) zmq_assert (!inpipe);
inpipe->term (); zmq_assert (!outpipe);
if (outpipe)
outpipe->term ();
} }
void zmq::pair_t::xattach_pipes (class reader_t *inpipe_, void zmq::pair_t::xattach_pipes (class reader_t *inpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) class writer_t *outpipe_, const blob_t &peer_identity_)
{ {
zmq_assert (!inpipe && !outpipe); zmq_assert (!inpipe && !outpipe);
inpipe = inpipe_; inpipe = inpipe_;
inpipe_alive = true;
inpipe->set_event_sink (this);
outpipe = outpipe_; outpipe = outpipe_;
outpipe_alive = true; outpipe_alive = true;
outpipe->set_event_sink (this);
} }
void zmq::pair_t::xdetach_inpipe (class reader_t *pipe_) void zmq::pair_t::terminated (class reader_t *pipe_)
{ {
zmq_assert (pipe_ == inpipe); zmq_assert (pipe_ == inpipe);
inpipe = NULL; inpipe = NULL;
inpipe_alive = false;
} }
void zmq::pair_t::xdetach_outpipe (class writer_t *pipe_) void zmq::pair_t::terminated (class writer_t *pipe_)
{ {
zmq_assert (pipe_ == outpipe); zmq_assert (pipe_ == outpipe);
outpipe = NULL; outpipe = NULL;
outpipe_alive = false;
} }
void zmq::pair_t::xkill (class reader_t *pipe_) void zmq::pair_t::xterm_pipes ()
{ {
zmq_assert (alive); if (inpipe)
alive = false; inpipe->terminate ();
if (outpipe)
outpipe->terminate ();
} }
void zmq::pair_t::xrevive (class reader_t *pipe_) bool zmq::pair_t::xhas_pipes ()
{ {
zmq_assert (!alive); return inpipe != NULL || outpipe != NULL;
alive = true;
} }
void zmq::pair_t::xrevive (class writer_t *pipe_) void zmq::pair_t::activated (class reader_t *pipe_)
{ {
zmq_assert (!outpipe_alive); zmq_assert (!inpipe_alive);
outpipe_alive = true; inpipe_alive = true;
} }
int zmq::pair_t::xsetsockopt (int option_, const void *optval_, void zmq::pair_t::activated (class writer_t *pipe_)
size_t optvallen_)
{ {
errno = EINVAL; zmq_assert (!outpipe_alive);
return -1; outpipe_alive = true;
} }
int zmq::pair_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::pair_t::xsend (zmq_msg_t *msg_, int flags_)
...@@ -100,6 +106,7 @@ int zmq::pair_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -100,6 +106,7 @@ int zmq::pair_t::xsend (zmq_msg_t *msg_, int flags_)
return -1; return -1;
} }
if (!(flags_ & ZMQ_SNDMORE))
outpipe->flush (); outpipe->flush ();
// Detach the original message from the data buffer. // Detach the original message from the data buffer.
...@@ -114,9 +121,12 @@ int zmq::pair_t::xrecv (zmq_msg_t *msg_, int flags_) ...@@ -114,9 +121,12 @@ int zmq::pair_t::xrecv (zmq_msg_t *msg_, int flags_)
// Deallocate old content of the message. // Deallocate old content of the message.
zmq_msg_close (msg_); zmq_msg_close (msg_);
if (!alive || !inpipe || !inpipe->read (msg_)) { if (!inpipe_alive || !inpipe || !inpipe->read (msg_)) {
// No message is available. Initialise the output parameter
// to be a 0-byte message. // No message is available.
inpipe_alive = false;
// Initialise the output parameter to be a 0-byte message.
zmq_msg_init (msg_); zmq_msg_init (msg_);
errno = EAGAIN; errno = EAGAIN;
return -1; return -1;
...@@ -126,14 +136,16 @@ int zmq::pair_t::xrecv (zmq_msg_t *msg_, int flags_) ...@@ -126,14 +136,16 @@ int zmq::pair_t::xrecv (zmq_msg_t *msg_, int flags_)
bool zmq::pair_t::xhas_in () bool zmq::pair_t::xhas_in ()
{ {
if (alive && inpipe && inpipe->check_read ()) if (!inpipe || !inpipe_alive)
return true;
return false; return false;
inpipe_alive = inpipe->check_read ();
return inpipe_alive;
} }
bool zmq::pair_t::xhas_out () bool zmq::pair_t::xhas_out ()
{ {
if (outpipe == NULL || !outpipe_alive) if (!outpipe || !outpipe_alive)
return false; return false;
outpipe_alive = outpipe->check_write (); outpipe_alive = outpipe->check_write ();
......
...@@ -21,37 +21,45 @@ ...@@ -21,37 +21,45 @@
#define __ZMQ_PAIR_HPP_INCLUDED__ #define __ZMQ_PAIR_HPP_INCLUDED__
#include "socket_base.hpp" #include "socket_base.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
class pair_t : public socket_base_t class pair_t :
public socket_base_t,
public i_reader_events,
public i_writer_events
{ {
public: public:
pair_t (class app_thread_t *parent_); pair_t (class ctx_t *parent_, uint32_t slot_);
~pair_t (); ~pair_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out (); bool xhas_out ();
// i_reader_events interface implementation.
void activated (class reader_t *pipe_);
void terminated (class reader_t *pipe_);
// i_writer_events interface implementation.
void activated (class writer_t *pipe_);
void terminated (class writer_t *pipe_);
private: private:
class reader_t *inpipe; class reader_t *inpipe;
class writer_t *outpipe; class writer_t *outpipe;
bool alive; bool inpipe_alive;
bool outpipe_alive; bool outpipe_alive;
pair_t (const pair_t&); pair_t (const pair_t&);
......
This diff is collapsed.
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
#include "../include/zmq.h" #include "../include/zmq.h"
#include "stdint.hpp" #include "stdint.hpp"
#include "i_endpoint.hpp"
#include "yarray_item.hpp" #include "yarray_item.hpp"
#include "ypipe.hpp" #include "ypipe.hpp"
#include "msg_store.hpp" #include "msg_store.hpp"
...@@ -33,15 +32,31 @@ ...@@ -33,15 +32,31 @@
namespace zmq namespace zmq
{ {
// The shutdown mechanism for pipe works as follows: Either endpoint
// (or even both of them) can ask pipe to terminate by calling 'terminate'
// method. Pipe then terminates in asynchronous manner. When the part of
// the shutdown tied to the endpoint is done it triggers 'terminated'
// event. When endpoint processes the event and returns, associated
// reader/writer object is deallocated.
typedef ypipe_t <zmq_msg_t, message_pipe_granularity> pipe_t;
struct i_reader_events
{
virtual void terminated (class reader_t *pipe_) = 0;
virtual void activated (class reader_t *pipe_) = 0;
};
class reader_t : public object_t, public yarray_item_t class reader_t : public object_t, public yarray_item_t
{ {
public: friend void zmq::create_pipe (object_t*, object_t*, uint64_t,
int64_t, reader_t**, writer_t**);
friend class writer_t;
reader_t (class object_t *parent_, uint64_t lwm_); public:
~reader_t ();
void set_pipe (class pipe_t *pipe_); // Specifies the object to get events from the reader.
void set_endpoint (i_endpoint *endpoint_); void set_event_sink (i_reader_events *endpoint_);
// Returns true if there is at least one message to read in the pipe. // Returns true if there is at least one message to read in the pipe.
bool check_read (); bool check_read ();
...@@ -50,10 +65,20 @@ namespace zmq ...@@ -50,10 +65,20 @@ namespace zmq
bool read (zmq_msg_t *msg_); bool read (zmq_msg_t *msg_);
// Ask pipe to terminate. // Ask pipe to terminate.
void term (); void terminate ();
// Returns true if the pipe is already terminating
// (say if delimiter was already read).
bool is_terminating ();
private: private:
reader_t (class object_t *parent_, pipe_t *pipe_, uint64_t lwm_);
~reader_t ();
// To be called only by writer itself!
void set_writer (class writer_t *writer_);
// Command handlers. // Command handlers.
void process_revive (); void process_revive ();
void process_pipe_term_ack (); void process_pipe_term_ack ();
...@@ -62,10 +87,10 @@ namespace zmq ...@@ -62,10 +87,10 @@ namespace zmq
static bool is_delimiter (zmq_msg_t &msg_); static bool is_delimiter (zmq_msg_t &msg_);
// The underlying pipe. // The underlying pipe.
class pipe_t *pipe; pipe_t *pipe;
// Pipe writer associated with the other side of the pipe. // Pipe writer associated with the other side of the pipe.
class writer_t *peer; class writer_t *writer;
// Low watermark for in-memory storage (in bytes). // Low watermark for in-memory storage (in bytes).
uint64_t lwm; uint64_t lwm;
...@@ -73,22 +98,32 @@ namespace zmq ...@@ -73,22 +98,32 @@ namespace zmq
// Number of messages read so far. // Number of messages read so far.
uint64_t msgs_read; uint64_t msgs_read;
// Endpoint (either session or socket) the pipe is attached to. // Sink for the events (either the socket of the session).
i_endpoint *endpoint; i_reader_events *sink;
// True is 'terminate' method was called or delimiter
// was read from the pipe.
bool terminating;
reader_t (const reader_t&); reader_t (const reader_t&);
void operator = (const reader_t&); void operator = (const reader_t&);
}; };
struct i_writer_events
{
virtual void terminated (class writer_t *pipe_) = 0;
virtual void activated (class writer_t *pipe_) = 0;
};
class writer_t : public object_t, public yarray_item_t class writer_t : public object_t, public yarray_item_t
{ {
public: friend void zmq::create_pipe (object_t*, object_t*, uint64_t,
int64_t, reader_t**, writer_t**);
writer_t (class object_t *parent_, uint64_t hwm_, int64_t swap_size_); public:
~writer_t ();
void set_pipe (class pipe_t *pipe_); // Specifies the object to get events from the writer.
void set_endpoint (i_endpoint *endpoint_); void set_event_sink (i_writer_events *endpoint_);
// Checks whether a message can be written to the pipe. // Checks whether a message can be written to the pipe.
// If writing the message would cause high watermark to be // If writing the message would cause high watermark to be
...@@ -106,10 +141,14 @@ namespace zmq ...@@ -106,10 +141,14 @@ namespace zmq
void flush (); void flush ();
// Ask pipe to terminate. // Ask pipe to terminate.
void term (); void terminate ();
private: private:
writer_t (class object_t *parent_, pipe_t *pipe_, reader_t *reader_,
uint64_t hwm_, int64_t swap_size_);
~writer_t ();
void process_reader_info (uint64_t msgs_read_); void process_reader_info (uint64_t msgs_read_);
// Command handlers. // Command handlers.
...@@ -123,10 +162,10 @@ namespace zmq ...@@ -123,10 +162,10 @@ namespace zmq
void write_delimiter (); void write_delimiter ();
// The underlying pipe. // The underlying pipe.
class pipe_t *pipe; pipe_t *pipe;
// Pipe reader associated with the other side of the pipe. // Pipe reader associated with the other side of the pipe.
class reader_t *peer; reader_t *reader;
// High watermark for in-memory storage (in bytes). // High watermark for in-memory storage (in bytes).
uint64_t hwm; uint64_t hwm;
...@@ -149,35 +188,23 @@ namespace zmq ...@@ -149,35 +188,23 @@ namespace zmq
// True iff the last attempt to write a message has failed. // True iff the last attempt to write a message has failed.
bool stalled; bool stalled;
bool pending_close; // Sink for the events (either the socket or the session).
i_writer_events *sink;
// Endpoint (either session or socket) the pipe is attached to. // True is 'terminate' method was called of 'pipe_term' command
i_endpoint *endpoint; // arrived from the reader.
bool terminating;
bool pending_close;
writer_t (const writer_t&); writer_t (const writer_t&);
void operator = (const writer_t&); void operator = (const writer_t&);
}; };
// Message pipe. // Creates a pipe. Returns pointer to reader and writer objects.
class pipe_t : public ypipe_t <zmq_msg_t, message_pipe_granularity> void create_pipe (object_t *reader_parent_, object_t *writer_parent_,
{ uint64_t hwm_, int64_t swap_size_, reader_t **reader_,
public: writer_t **writer_);
pipe_t (object_t *reader_parent_, object_t *writer_parent_,
uint64_t hwm_, int64_t swap_size_);
~pipe_t ();
reader_t reader;
writer_t writer;
private:
uint64_t compute_lwm (uint64_t hwm_);
pipe_t (const pipe_t&);
void operator = (const pipe_t&);
};
} }
#endif #endif
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
#include "msg_content.hpp" #include "msg_content.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::pub_t::pub_t (class app_thread_t *parent_) : zmq::pub_t::pub_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
active (0) active (0)
{ {
options.requires_in = false; options.requires_in = false;
...@@ -34,56 +34,47 @@ zmq::pub_t::pub_t (class app_thread_t *parent_) : ...@@ -34,56 +34,47 @@ zmq::pub_t::pub_t (class app_thread_t *parent_) :
zmq::pub_t::~pub_t () zmq::pub_t::~pub_t ()
{ {
for (pipes_t::size_type i = 0; i != pipes.size (); i++) zmq_assert (pipes.empty ());
pipes [i]->term ();
pipes.clear ();
} }
void zmq::pub_t::xattach_pipes (class reader_t *inpipe_, void zmq::pub_t::xattach_pipes (class reader_t *inpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) class writer_t *outpipe_, const blob_t &peer_identity_)
{ {
zmq_assert (!inpipe_); zmq_assert (!inpipe_);
outpipe_->set_event_sink (this);
pipes.push_back (outpipe_); pipes.push_back (outpipe_);
pipes.swap (active, pipes.size () - 1); pipes.swap (active, pipes.size () - 1);
active++; active++;
} }
void zmq::pub_t::xdetach_inpipe (class reader_t *pipe_) void zmq::pub_t::xterm_pipes ()
{
zmq_assert (false);
}
void zmq::pub_t::xdetach_outpipe (class writer_t *pipe_)
{
// Remove the pipe from the list; adjust number of active pipes
// accordingly.
if (pipes.index (pipe_) < active)
active--;
pipes.erase (pipe_);
}
void zmq::pub_t::xkill (class reader_t *pipe_)
{ {
zmq_assert (false); // Start shutdown process for all the pipes.
for (pipes_t::size_type i = 0; i != pipes.size (); i++)
pipes [i]->terminate ();
} }
void zmq::pub_t::xrevive (class reader_t *pipe_) bool zmq::pub_t::xhas_pipes ()
{ {
zmq_assert (false); return !pipes.empty ();
} }
void zmq::pub_t::xrevive (class writer_t *pipe_) void zmq::pub_t::activated (writer_t *pipe_)
{ {
// Move the pipe to the list of active pipes. // Move the pipe to the list of active pipes.
pipes.swap (pipes.index (pipe_), active); pipes.swap (pipes.index (pipe_), active);
active++; active++;
} }
int zmq::pub_t::xsetsockopt (int option_, const void *optval_, void zmq::pub_t::terminated (writer_t *pipe_)
size_t optvallen_)
{ {
errno = EINVAL; // Remove the pipe from the list; adjust number of active pipes
return -1; // accordingly.
if (pipes.index (pipe_) < active)
active--;
pipes.erase (pipe_);
} }
int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_)
...@@ -101,7 +92,7 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -101,7 +92,7 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_)
// For VSMs the copying is straighforward. // For VSMs the copying is straighforward.
if (content == (msg_content_t*) ZMQ_VSM) { if (content == (msg_content_t*) ZMQ_VSM) {
for (pipes_t::size_type i = 0; i != active;) for (pipes_t::size_type i = 0; i < active;)
if (write (pipes [i], msg_)) if (write (pipes [i], msg_))
i++; i++;
int rc = zmq_msg_init (msg_); int rc = zmq_msg_init (msg_);
...@@ -133,7 +124,7 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -133,7 +124,7 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_)
} }
// Push the message to all destinations. // Push the message to all destinations.
for (pipes_t::size_type i = 0; i != active;) { for (pipes_t::size_type i = 0; i < active;) {
if (!write (pipes [i], msg_)) if (!write (pipes [i], msg_))
content->refcnt.sub (1); content->refcnt.sub (1);
else else
...@@ -147,17 +138,6 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -147,17 +138,6 @@ int zmq::pub_t::xsend (zmq_msg_t *msg_, int flags_)
return 0; return 0;
} }
int zmq::pub_t::xrecv (zmq_msg_t *msg_, int flags_)
{
errno = ENOTSUP;
return -1;
}
bool zmq::pub_t::xhas_in ()
{
return false;
}
bool zmq::pub_t::xhas_out () bool zmq::pub_t::xhas_out ()
{ {
return true; return true;
......
...@@ -22,31 +22,30 @@ ...@@ -22,31 +22,30 @@
#include "socket_base.hpp" #include "socket_base.hpp"
#include "yarray.hpp" #include "yarray.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
class pub_t : public socket_base_t class pub_t : public socket_base_t, public i_writer_events
{ {
public: public:
pub_t (class app_thread_t *parent_); pub_t (class ctx_t *parent_, uint32_t slot_);
~pub_t (); ~pub_t ();
// Overloads of functions from socket_base_t. // Implementations of virtual functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in ();
bool xhas_out (); bool xhas_out ();
// i_writer_events interface implementation.
void activated (writer_t *pipe_);
void terminated (writer_t *pipe_);
private: private:
// Write the message to the pipe. Make the pipe inactive if writing // Write the message to the pipe. Make the pipe inactive if writing
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#include "pull.hpp" #include "pull.hpp"
#include "err.hpp" #include "err.hpp"
zmq::pull_t::pull_t (class app_thread_t *parent_) : zmq::pull_t::pull_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_) socket_base_t (parent_, slot_)
{ {
options.requires_in = true; options.requires_in = true;
options.requires_out = false; options.requires_out = false;
...@@ -40,45 +40,14 @@ void zmq::pull_t::xattach_pipes (class reader_t *inpipe_, ...@@ -40,45 +40,14 @@ void zmq::pull_t::xattach_pipes (class reader_t *inpipe_,
fq.attach (inpipe_); fq.attach (inpipe_);
} }
void zmq::pull_t::xdetach_inpipe (class reader_t *pipe_) void zmq::pull_t::xterm_pipes ()
{ {
zmq_assert (pipe_); fq.term_pipes ();
fq.detach (pipe_);
} }
void zmq::pull_t::xdetach_outpipe (class writer_t *pipe_) bool zmq::pull_t::xhas_pipes ()
{ {
// There are no outpipes, so this function shouldn't be called at all. return fq.has_pipes ();
zmq_assert (false);
}
void zmq::pull_t::xkill (class reader_t *pipe_)
{
fq.kill (pipe_);
}
void zmq::pull_t::xrevive (class reader_t *pipe_)
{
fq.revive (pipe_);
}
void zmq::pull_t::xrevive (class writer_t *pipe_)
{
zmq_assert (false);
}
int zmq::pull_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{
// No special options for this socket type.
errno = EINVAL;
return -1;
}
int zmq::pull_t::xsend (zmq_msg_t *msg_, int flags_)
{
errno = ENOTSUP;
return -1;
} }
int zmq::pull_t::xrecv (zmq_msg_t *msg_, int flags_) int zmq::pull_t::xrecv (zmq_msg_t *msg_, int flags_)
...@@ -91,8 +60,3 @@ bool zmq::pull_t::xhas_in () ...@@ -91,8 +60,3 @@ bool zmq::pull_t::xhas_in ()
return fq.has_in (); return fq.has_in ();
} }
bool zmq::pull_t::xhas_out ()
{
return false;
}
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __ZMQ_PULL_HPP_INCLUDED__ #ifndef __ZMQ_UPSTREAM_HPP_INCLUDED__
#define __ZMQ_PULL_HPP_INCLUDED__ #define __ZMQ_UPSTREAM_HPP_INCLUDED__
#include "socket_base.hpp" #include "socket_base.hpp"
#include "fq.hpp" #include "fq.hpp"
...@@ -30,22 +30,16 @@ namespace zmq ...@@ -30,22 +30,16 @@ namespace zmq
{ {
public: public:
pull_t (class app_thread_t *parent_); pull_t (class ctx_t *parent_, uint32_t slot_);
~pull_t (); ~pull_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out ();
private: private:
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include "err.hpp" #include "err.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::push_t::push_t (class app_thread_t *parent_) : zmq::push_t::push_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_) socket_base_t (parent_, slot_)
{ {
options.requires_in = false; options.requires_in = false;
options.requires_out = true; options.requires_out = true;
...@@ -41,41 +41,14 @@ void zmq::push_t::xattach_pipes (class reader_t *inpipe_, ...@@ -41,41 +41,14 @@ void zmq::push_t::xattach_pipes (class reader_t *inpipe_,
lb.attach (outpipe_); lb.attach (outpipe_);
} }
void zmq::push_t::xdetach_inpipe (class reader_t *pipe_) void zmq::push_t::xterm_pipes ()
{ {
// There are no inpipes, so this function shouldn't be called at all. lb.term_pipes ();
zmq_assert (false);
} }
void zmq::push_t::xdetach_outpipe (class writer_t *pipe_) bool zmq::push_t::xhas_pipes ()
{ {
zmq_assert (pipe_); return lb.has_pipes ();
lb.detach (pipe_);
}
void zmq::push_t::xkill (class reader_t *pipe_)
{
// There are no inpipes, so this function shouldn't be called at all.
zmq_assert (false);
}
void zmq::push_t::xrevive (class reader_t *pipe_)
{
// There are no inpipes, so this function shouldn't be called at all.
zmq_assert (false);
}
void zmq::push_t::xrevive (class writer_t *pipe_)
{
lb.revive (pipe_);
}
int zmq::push_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{
// No special option for this socket type.
errno = EINVAL;
return -1;
} }
int zmq::push_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::push_t::xsend (zmq_msg_t *msg_, int flags_)
...@@ -83,17 +56,6 @@ int zmq::push_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -83,17 +56,6 @@ int zmq::push_t::xsend (zmq_msg_t *msg_, int flags_)
return lb.send (msg_, flags_); return lb.send (msg_, flags_);
} }
int zmq::push_t::xrecv (zmq_msg_t *msg_, int flags_)
{
errno = ENOTSUP;
return -1;
}
bool zmq::push_t::xhas_in ()
{
return false;
}
bool zmq::push_t::xhas_out () bool zmq::push_t::xhas_out ()
{ {
return lb.has_out (); return lb.has_out ();
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __ZMQ_PUSH_HPP_INCLUDED__ #ifndef __ZMQ_DOWNSTREAM_HPP_INCLUDED__
#define __ZMQ_PUSH_HPP_INCLUDED__ #define __ZMQ_DOWNSTREAM_HPP_INCLUDED__
#include "socket_base.hpp" #include "socket_base.hpp"
#include "lb.hpp" #include "lb.hpp"
...@@ -30,21 +30,15 @@ namespace zmq ...@@ -30,21 +30,15 @@ namespace zmq
{ {
public: public:
push_t (class app_thread_t *parent_); push_t (class ctx_t *parent_, uint32_t slot_);
~push_t (); ~push_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in ();
bool xhas_out (); bool xhas_out ();
private: private:
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include "err.hpp" #include "err.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::rep_t::rep_t (class app_thread_t *parent_) : zmq::rep_t::rep_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
active (0), active (0),
current (0), current (0),
sending_reply (false), sending_reply (false),
...@@ -42,6 +42,8 @@ zmq::rep_t::rep_t (class app_thread_t *parent_) : ...@@ -42,6 +42,8 @@ zmq::rep_t::rep_t (class app_thread_t *parent_) :
zmq::rep_t::~rep_t () zmq::rep_t::~rep_t ()
{ {
zmq_assert (in_pipes.empty ());
zmq_assert (out_pipes.empty ());
} }
void zmq::rep_t::xattach_pipes (class reader_t *inpipe_, void zmq::rep_t::xattach_pipes (class reader_t *inpipe_,
...@@ -50,15 +52,28 @@ void zmq::rep_t::xattach_pipes (class reader_t *inpipe_, ...@@ -50,15 +52,28 @@ void zmq::rep_t::xattach_pipes (class reader_t *inpipe_,
zmq_assert (inpipe_ && outpipe_); zmq_assert (inpipe_ && outpipe_);
zmq_assert (in_pipes.size () == out_pipes.size ()); zmq_assert (in_pipes.size () == out_pipes.size ());
inpipe_->set_event_sink (this);
in_pipes.push_back (inpipe_); in_pipes.push_back (inpipe_);
in_pipes.swap (active, in_pipes.size () - 1); in_pipes.swap (active, in_pipes.size () - 1);
outpipe_->set_event_sink (this);
out_pipes.push_back (outpipe_); out_pipes.push_back (outpipe_);
out_pipes.swap (active, out_pipes.size () - 1); out_pipes.swap (active, out_pipes.size () - 1);
active++; active++;
} }
void zmq::rep_t::xdetach_inpipe (class reader_t *pipe_) void zmq::rep_t::xterm_pipes ()
{
for (in_pipes_t::size_type i = 0; i != in_pipes.size (); i++)
in_pipes [i]->terminate ();
for (out_pipes_t::size_type i = 0; i != out_pipes.size (); i++)
out_pipes [i]->terminate ();
}
void zmq::rep_t::terminated (reader_t *pipe_)
{ {
// ???
zmq_assert (sending_reply || !more || in_pipes [current] != pipe_); zmq_assert (sending_reply || !more || in_pipes [current] != pipe_);
zmq_assert (pipe_); zmq_assert (pipe_);
...@@ -71,14 +86,17 @@ void zmq::rep_t::xdetach_inpipe (class reader_t *pipe_) ...@@ -71,14 +86,17 @@ void zmq::rep_t::xdetach_inpipe (class reader_t *pipe_)
if (current == active) if (current == active)
current = 0; current = 0;
} }
in_pipes.erase (index);
// ???
if (!zombie) {
if (out_pipes [index]) if (out_pipes [index])
out_pipes [index]->term (); out_pipes [index]->terminate ();
in_pipes.erase (index);
out_pipes.erase (index); out_pipes.erase (index);
}
} }
void zmq::rep_t::xdetach_outpipe (class writer_t *pipe_) void zmq::rep_t::terminated (writer_t *pipe_)
{ {
zmq_assert (pipe_); zmq_assert (pipe_);
zmq_assert (in_pipes.size () == out_pipes.size ()); zmq_assert (in_pipes.size () == out_pipes.size ());
...@@ -97,22 +115,22 @@ void zmq::rep_t::xdetach_outpipe (class writer_t *pipe_) ...@@ -97,22 +115,22 @@ void zmq::rep_t::xdetach_outpipe (class writer_t *pipe_)
current = 0; current = 0;
} }
out_pipes.erase (index);
// ???
if (!zombie) {
if (in_pipes [index]) if (in_pipes [index])
in_pipes [index]->term (); in_pipes [index]->terminate ();
in_pipes.erase (index); in_pipes.erase (index);
out_pipes.erase (index); }
} }
void zmq::rep_t::xkill (class reader_t *pipe_) bool zmq::rep_t::xhas_pipes ()
{ {
// Move the pipe to the list of inactive pipes. return !in_pipes.empty () || !out_pipes.empty ();
in_pipes_t::size_type index = in_pipes.index (pipe_);
active--;
in_pipes.swap (index, active);
out_pipes.swap (index, active);
} }
void zmq::rep_t::xrevive (class reader_t *pipe_) void zmq::rep_t::activated (reader_t *pipe_)
{ {
// Move the pipe to the list of active pipes. // Move the pipe to the list of active pipes.
in_pipes_t::size_type index = in_pipes.index (pipe_); in_pipes_t::size_type index = in_pipes.index (pipe_);
...@@ -121,15 +139,10 @@ void zmq::rep_t::xrevive (class reader_t *pipe_) ...@@ -121,15 +139,10 @@ void zmq::rep_t::xrevive (class reader_t *pipe_)
active++; active++;
} }
void zmq::rep_t::xrevive (class writer_t *pipe_) void zmq::rep_t::activated (writer_t *pipe_)
{
}
int zmq::rep_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{ {
errno = EINVAL; // TODO: What here?
return -1; zmq_assert (false);
} }
int zmq::rep_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::rep_t::xsend (zmq_msg_t *msg_, int flags_)
...@@ -151,6 +164,8 @@ int zmq::rep_t::xsend (zmq_msg_t *msg_, int flags_) ...@@ -151,6 +164,8 @@ int zmq::rep_t::xsend (zmq_msg_t *msg_, int flags_)
// misbehaving requesters stop collecting replies. // misbehaving requesters stop collecting replies.
// TODO: Tear down the underlying connection (?) // TODO: Tear down the underlying connection (?)
if (!written) { if (!written) {
// TODO: The reply socket becomes deactivated here...
errno = EAGAIN; errno = EAGAIN;
return -1; return -1;
} }
...@@ -198,6 +213,13 @@ int zmq::rep_t::xrecv (zmq_msg_t *msg_, int flags_) ...@@ -198,6 +213,13 @@ int zmq::rep_t::xrecv (zmq_msg_t *msg_, int flags_)
for (count = active; count != 0; count--) { for (count = active; count != 0; count--) {
if (in_pipes [current]->read (msg_)) if (in_pipes [current]->read (msg_))
break; break;
// Move the pipe to the list of inactive pipes.
active--;
in_pipes.swap (current, active);
out_pipes.swap (current, active);
// Move to next pipe.
current++; current++;
if (current >= active) if (current >= active)
current = 0; current = 0;
...@@ -258,6 +280,13 @@ bool zmq::rep_t::xhas_in () ...@@ -258,6 +280,13 @@ bool zmq::rep_t::xhas_in ()
for (int count = active; count != 0; count--) { for (int count = active; count != 0; count--) {
if (in_pipes [current]->check_read ()) if (in_pipes [current]->check_read ())
return !sending_reply; return !sending_reply;
// Move the pipe to the list of inactive pipes.
active--;
in_pipes.swap (current, active);
out_pipes.swap (current, active);
// Move to the next pipe.
current++; current++;
if (current >= active) if (current >= active)
current = 0; current = 0;
......
...@@ -22,39 +22,47 @@ ...@@ -22,39 +22,47 @@
#include "socket_base.hpp" #include "socket_base.hpp"
#include "yarray.hpp" #include "yarray.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
class rep_t : public socket_base_t class rep_t :
public socket_base_t,
public i_reader_events,
public i_writer_events
{ {
public: public:
rep_t (class app_thread_t *parent_); rep_t (class ctx_t *parent_, uint32_t slot_);
~rep_t (); ~rep_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out (); bool xhas_out ();
// i_reader_events interface implementation.
void activated (reader_t *pipe_);
void terminated (reader_t *pipe_);
// i_writer_events interface implementation.
void activated (writer_t *pipe_);
void terminated (writer_t *pipe_);
private: private:
// List in outbound and inbound pipes. Note that the two lists are // List in outbound and inbound pipes. Note that the two lists are
// always in sync. I.e. outpipe with index N communicates with the // always in sync. I.e. outpipe with index N communicates with the
// same session as inpipe with index N. // same session as inpipe with index N.
typedef yarray_t <class writer_t> out_pipes_t; typedef yarray_t <writer_t> out_pipes_t;
out_pipes_t out_pipes; out_pipes_t out_pipes;
typedef yarray_t <class reader_t> in_pipes_t; typedef yarray_t <reader_t> in_pipes_t;
in_pipes_t in_pipes; in_pipes_t in_pipes;
// Number of active inpipes. All the active inpipes are located at the // Number of active inpipes. All the active inpipes are located at the
...@@ -73,7 +81,7 @@ namespace zmq ...@@ -73,7 +81,7 @@ namespace zmq
bool more; bool more;
// Pipe we are going to send reply to. // Pipe we are going to send reply to.
class writer_t *reply_pipe; writer_t *reply_pipe;
rep_t (const rep_t&); rep_t (const rep_t&);
void operator = (const rep_t&); void operator = (const rep_t&);
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include "err.hpp" #include "err.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::req_t::req_t (class app_thread_t *parent_) : zmq::req_t::req_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
active (0), active (0),
current (0), current (0),
receiving_reply (false), receiving_reply (false),
...@@ -38,24 +38,36 @@ zmq::req_t::req_t (class app_thread_t *parent_) : ...@@ -38,24 +38,36 @@ zmq::req_t::req_t (class app_thread_t *parent_) :
zmq::req_t::~req_t () zmq::req_t::~req_t ()
{ {
zmq_assert (in_pipes.empty ());
zmq_assert (out_pipes.empty ());
} }
void zmq::req_t::xattach_pipes (class reader_t *inpipe_, void zmq::req_t::xattach_pipes (reader_t *inpipe_, writer_t *outpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) const blob_t &peer_identity_)
{ {
zmq_assert (inpipe_ && outpipe_); zmq_assert (inpipe_ && outpipe_);
zmq_assert (in_pipes.size () == out_pipes.size ()); zmq_assert (in_pipes.size () == out_pipes.size ());
inpipe_->set_event_sink (this);
in_pipes.push_back (inpipe_); in_pipes.push_back (inpipe_);
in_pipes.swap (active, in_pipes.size () - 1); in_pipes.swap (active, in_pipes.size () - 1);
outpipe_->set_event_sink (this);
out_pipes.push_back (outpipe_); out_pipes.push_back (outpipe_);
out_pipes.swap (active, out_pipes.size () - 1); out_pipes.swap (active, out_pipes.size () - 1);
active++; active++;
} }
void zmq::req_t::xdetach_inpipe (class reader_t *pipe_) void zmq::req_t::xterm_pipes ()
{
for (in_pipes_t::size_type i = 0; i != in_pipes.size (); i++)
in_pipes [i]->terminate ();
for (out_pipes_t::size_type i = 0; i != out_pipes.size (); i++)
out_pipes [i]->terminate ();
}
void zmq::req_t::terminated (reader_t *pipe_)
{ {
zmq_assert (!receiving_reply || !more || reply_pipe != pipe_); zmq_assert (!receiving_reply || !more || reply_pipe != pipe_);
...@@ -63,17 +75,21 @@ void zmq::req_t::xdetach_inpipe (class reader_t *pipe_) ...@@ -63,17 +75,21 @@ void zmq::req_t::xdetach_inpipe (class reader_t *pipe_)
zmq_assert (in_pipes.size () == out_pipes.size ()); zmq_assert (in_pipes.size () == out_pipes.size ());
// TODO: The pipe we are awaiting the reply from is detached. What now? // TODO: The pipe we are awaiting the reply from is detached. What now?
// Return ECONNRESET from subsequent recv?
if (receiving_reply && pipe_ == reply_pipe) { if (receiving_reply && pipe_ == reply_pipe) {
zmq_assert (false); zmq_assert (false);
} }
in_pipes_t::size_type index = in_pipes.index (pipe_); in_pipes_t::size_type index = in_pipes.index (pipe_);
// ???
if (!zombie) {
if (out_pipes [index]) if (out_pipes [index])
out_pipes [index]->term (); out_pipes [index]->terminate ();
in_pipes.erase (index);
out_pipes.erase (index); out_pipes.erase (index);
}
in_pipes.erase (index);
if (index < active) { if (index < active) {
active--; active--;
if (current == active) if (current == active)
...@@ -81,7 +97,7 @@ void zmq::req_t::xdetach_inpipe (class reader_t *pipe_) ...@@ -81,7 +97,7 @@ void zmq::req_t::xdetach_inpipe (class reader_t *pipe_)
} }
} }
void zmq::req_t::xdetach_outpipe (class writer_t *pipe_) void zmq::req_t::terminated (writer_t *pipe_)
{ {
zmq_assert (receiving_reply || !more || out_pipes [current] != pipe_); zmq_assert (receiving_reply || !more || out_pipes [current] != pipe_);
...@@ -90,9 +106,13 @@ void zmq::req_t::xdetach_outpipe (class writer_t *pipe_) ...@@ -90,9 +106,13 @@ void zmq::req_t::xdetach_outpipe (class writer_t *pipe_)
out_pipes_t::size_type index = out_pipes.index (pipe_); out_pipes_t::size_type index = out_pipes.index (pipe_);
// ???
if (!zombie) {
if (in_pipes [index]) if (in_pipes [index])
in_pipes [index]->term (); in_pipes [index]->terminate ();
in_pipes.erase (index); in_pipes.erase (index);
}
out_pipes.erase (index); out_pipes.erase (index);
if (index < active) { if (index < active) {
active--; active--;
...@@ -101,15 +121,12 @@ void zmq::req_t::xdetach_outpipe (class writer_t *pipe_) ...@@ -101,15 +121,12 @@ void zmq::req_t::xdetach_outpipe (class writer_t *pipe_)
} }
} }
void zmq::req_t::xkill (class reader_t *pipe_) bool zmq::req_t::xhas_pipes ()
{ {
zmq_assert (receiving_reply); return !in_pipes.empty () || !out_pipes.empty ();
zmq_assert (pipe_ == reply_pipe);
reply_pipe_active = false;
} }
void zmq::req_t::xrevive (class reader_t *pipe_) void zmq::req_t::activated (reader_t *pipe_)
{ {
// TODO: Actually, misbehaving peer can cause this kind of thing. // TODO: Actually, misbehaving peer can cause this kind of thing.
// Handle it decently, presumably kill the offending connection. // Handle it decently, presumably kill the offending connection.
...@@ -117,7 +134,7 @@ void zmq::req_t::xrevive (class reader_t *pipe_) ...@@ -117,7 +134,7 @@ void zmq::req_t::xrevive (class reader_t *pipe_)
reply_pipe_active = true; reply_pipe_active = true;
} }
void zmq::req_t::xrevive (class writer_t *pipe_) void zmq::req_t::activated (writer_t *pipe_)
{ {
out_pipes_t::size_type index = out_pipes.index (pipe_); out_pipes_t::size_type index = out_pipes.index (pipe_);
zmq_assert (index >= active); zmq_assert (index >= active);
...@@ -129,13 +146,6 @@ void zmq::req_t::xrevive (class writer_t *pipe_) ...@@ -129,13 +146,6 @@ void zmq::req_t::xrevive (class writer_t *pipe_)
} }
} }
int zmq::req_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{
errno = EINVAL;
return -1;
}
int zmq::req_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::req_t::xsend (zmq_msg_t *msg_, int flags_)
{ {
// If we've sent a request and we still haven't got the reply, // If we've sent a request and we still haven't got the reply,
...@@ -214,6 +224,7 @@ int zmq::req_t::xrecv (zmq_msg_t *msg_, int flags_) ...@@ -214,6 +224,7 @@ int zmq::req_t::xrecv (zmq_msg_t *msg_, int flags_)
// Get the reply from the reply pipe. // Get the reply from the reply pipe.
if (!reply_pipe_active || !reply_pipe->read (msg_)) { if (!reply_pipe_active || !reply_pipe->read (msg_)) {
reply_pipe_active = false;
zmq_msg_init (msg_); zmq_msg_init (msg_);
errno = EAGAIN; errno = EAGAIN;
return -1; return -1;
......
...@@ -22,31 +22,39 @@ ...@@ -22,31 +22,39 @@
#include "socket_base.hpp" #include "socket_base.hpp"
#include "yarray.hpp" #include "yarray.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
class req_t : public socket_base_t class req_t :
public socket_base_t,
public i_reader_events,
public i_writer_events
{ {
public: public:
req_t (class app_thread_t *parent_); req_t (class ctx_t *parent_, uint32_t slot_);
~req_t (); ~req_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (reader_t *inpipe_, writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out (); bool xhas_out ();
// i_reader_events interface implementation.
void activated (reader_t *pipe_);
void terminated (reader_t *pipe_);
// i_writer_events interface implementation.
void activated (writer_t *pipe_);
void terminated (writer_t *pipe_);
private: private:
// List in outbound and inbound pipes. Note that the two lists are // List in outbound and inbound pipes. Note that the two lists are
...@@ -58,9 +66,9 @@ namespace zmq ...@@ -58,9 +66,9 @@ namespace zmq
// the beginning of the array). We don't have to do the same thing for // the beginning of the array). We don't have to do the same thing for
// inpipes, because we know which pipe we want to read the // inpipes, because we know which pipe we want to read the
// reply from. // reply from.
typedef yarray_t <class writer_t> out_pipes_t; typedef yarray_t <writer_t> out_pipes_t;
out_pipes_t out_pipes; out_pipes_t out_pipes;
typedef yarray_t <class reader_t> in_pipes_t; typedef yarray_t <reader_t> in_pipes_t;
in_pipes_t in_pipes; in_pipes_t in_pipes;
// Number of active pipes. // Number of active pipes.
...@@ -82,7 +90,7 @@ namespace zmq ...@@ -82,7 +90,7 @@ namespace zmq
bool more; bool more;
// Pipe we are awaiting the reply from. // Pipe we are awaiting the reply from.
class reader_t *reply_pipe; reader_t *reply_pipe;
req_t (const req_t&); req_t (const req_t&);
void operator = (const req_t&); void operator = (const req_t&);
......
/*
Copyright (c) 2007-2010 iMatix Corporation
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the Lesser GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Lesser GNU General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_SEMAPHORE_HPP_INCLUDED__
#define __ZMQ_SEMAPHORE_HPP_INCLUDED__
#include "platform.hpp"
#include "err.hpp"
#if defined ZMQ_HAVE_WINDOWS
#include "windows.hpp"
#else
#include <semaphore.h>
#endif
namespace zmq
{
// 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 ZMQ_HAVE_WINDOWS
// On Windows platform simple semaphore is implemeted using event object.
class semaphore_t
{
public:
// Initialise the semaphore.
inline semaphore_t ()
{
ev = CreateEvent (NULL, FALSE, FALSE, NULL);
win_assert (ev != NULL);
}
// Destroy the semaphore.
inline ~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.
semaphore_t (const semaphore_t&);
void operator = (const semaphore_t&);
};
#else
// Default implementation maps simple semaphore to POSIX semaphore.
class semaphore_t
{
public:
// Initialise the semaphore.
inline semaphore_t ()
{
int rc = sem_init (&sem, 0, 0);
errno_assert (rc != -1);
}
// Destroy the semaphore.
inline ~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.
semaphore_t (const semaphore_t&);
void operator = (const semaphore_t&);
};
#endif
}
#endif
...@@ -69,13 +69,22 @@ zmq::session_t::~session_t () ...@@ -69,13 +69,22 @@ zmq::session_t::~session_t ()
zmq_assert (!out_pipe); zmq_assert (!out_pipe);
} }
bool zmq::session_t::is_terminable ()
{
return in_pipe->is_terminating ();
}
bool zmq::session_t::read (::zmq_msg_t *msg_) bool zmq::session_t::read (::zmq_msg_t *msg_)
{ {
if (!in_pipe || !active) if (!in_pipe || !active)
return false; return false;
if (!in_pipe->read (msg_)) if (!in_pipe->read (msg_)) {
active = false;
if (in_pipe->is_terminating ())
finalise ();
return false; return false;
}
incomplete_in = msg_->flags & ZMQ_MSG_MORE; incomplete_in = msg_->flags & ZMQ_MSG_MORE;
return true; return true;
...@@ -156,33 +165,28 @@ void zmq::session_t::attach_pipes (class reader_t *inpipe_, ...@@ -156,33 +165,28 @@ void zmq::session_t::attach_pipes (class reader_t *inpipe_,
zmq_assert (!in_pipe); zmq_assert (!in_pipe);
in_pipe = inpipe_; in_pipe = inpipe_;
active = true; active = true;
in_pipe->set_endpoint (this); in_pipe->set_event_sink (this);
} }
if (outpipe_) { if (outpipe_) {
zmq_assert (!out_pipe); zmq_assert (!out_pipe);
out_pipe = outpipe_; out_pipe = outpipe_;
out_pipe->set_endpoint (this); out_pipe->set_event_sink (this);
} }
} }
void zmq::session_t::detach_inpipe (reader_t *pipe_) void zmq::session_t::terminated (reader_t *pipe_)
{ {
active = false; active = false;
in_pipe = NULL; in_pipe = NULL;
} }
void zmq::session_t::detach_outpipe (writer_t *pipe_) void zmq::session_t::terminated (writer_t *pipe_)
{ {
out_pipe = NULL; out_pipe = NULL;
} }
void zmq::session_t::kill (reader_t *pipe_) void zmq::session_t::activated (reader_t *pipe_)
{
active = false;
}
void zmq::session_t::revive (reader_t *pipe_)
{ {
zmq_assert (in_pipe == pipe_); zmq_assert (in_pipe == pipe_);
active = true; active = true;
...@@ -190,7 +194,7 @@ void zmq::session_t::revive (reader_t *pipe_) ...@@ -190,7 +194,7 @@ void zmq::session_t::revive (reader_t *pipe_)
engine->revive (); engine->revive ();
} }
void zmq::session_t::revive (writer_t *pipe_) void zmq::session_t::activated (writer_t *pipe_)
{ {
zmq_assert (out_pipe == pipe_); zmq_assert (out_pipe == pipe_);
if (engine) if (engine)
...@@ -203,6 +207,11 @@ void zmq::session_t::process_plug () ...@@ -203,6 +207,11 @@ void zmq::session_t::process_plug ()
void zmq::session_t::process_unplug () void zmq::session_t::process_unplug ()
{ {
// TODO: There may be a problem here. The called ensures that all the
// commands on the fly have been delivered. However, given that the
// session is unregistered from the global repository only at this point
// there may be some commands being sent to the session right now.
// Unregister the session from the socket. // Unregister the session from the socket.
if (ordinal) if (ordinal)
owner->unregister_session (ordinal); owner->unregister_session (ordinal);
...@@ -210,14 +219,10 @@ void zmq::session_t::process_unplug () ...@@ -210,14 +219,10 @@ void zmq::session_t::process_unplug ()
owner->unregister_session (peer_identity); owner->unregister_session (peer_identity);
// Ask associated pipes to terminate. // Ask associated pipes to terminate.
if (in_pipe) { if (in_pipe)
in_pipe->term (); in_pipe->terminate ();
in_pipe = NULL; if (out_pipe)
} out_pipe->terminate ();
if (out_pipe) {
out_pipe->term ();
out_pipe = NULL;
}
if (engine) { if (engine) {
engine->unplug (); engine->unplug ();
...@@ -265,19 +270,15 @@ void zmq::session_t::process_attach (i_engine *engine_, ...@@ -265,19 +270,15 @@ void zmq::session_t::process_attach (i_engine *engine_,
writer_t *socket_writer = NULL; writer_t *socket_writer = NULL;
if (options.requires_in && !out_pipe) { if (options.requires_in && !out_pipe) {
pipe_t *pipe = new (std::nothrow) pipe_t (owner, this, options.hwm, options.swap); create_pipe (owner, this, options.hwm, options.swap, &socket_reader,
zmq_assert (pipe); &out_pipe);
out_pipe = &pipe->writer; out_pipe->set_event_sink (this);
out_pipe->set_endpoint (this);
socket_reader = &pipe->reader;
} }
if (options.requires_out && !in_pipe) { if (options.requires_out && !in_pipe) {
pipe_t *pipe = new (std::nothrow) pipe_t (this, owner, options.hwm, options.swap); create_pipe (this, owner, options.hwm, options.swap, &in_pipe,
zmq_assert (pipe); &socket_writer);
in_pipe = &pipe->reader; in_pipe->set_event_sink (this);
in_pipe->set_endpoint (this);
socket_writer = &pipe->writer;
} }
if (socket_reader || socket_writer) if (socket_reader || socket_writer)
...@@ -289,3 +290,4 @@ void zmq::session_t::process_attach (i_engine *engine_, ...@@ -289,3 +290,4 @@ void zmq::session_t::process_attach (i_engine *engine_,
engine = engine_; engine = engine_;
engine->plug (this); engine->plug (this);
} }
...@@ -21,15 +21,19 @@ ...@@ -21,15 +21,19 @@
#define __ZMQ_SESSION_HPP_INCLUDED__ #define __ZMQ_SESSION_HPP_INCLUDED__
#include "i_inout.hpp" #include "i_inout.hpp"
#include "i_endpoint.hpp"
#include "owned.hpp" #include "owned.hpp"
#include "options.hpp" #include "options.hpp"
#include "blob.hpp" #include "blob.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
class session_t : public owned_t, public i_inout, public i_endpoint class session_t :
public owned_t,
public i_inout,
public i_reader_events,
public i_writer_events
{ {
public: public:
...@@ -50,19 +54,25 @@ namespace zmq ...@@ -50,19 +54,25 @@ namespace zmq
class socket_base_t *get_owner (); class socket_base_t *get_owner ();
uint64_t get_ordinal (); uint64_t get_ordinal ();
// i_endpoint interface implementation.
void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void detach_inpipe (class reader_t *pipe_);
void detach_outpipe (class writer_t *pipe_); // i_reader_events interface implementation.
void kill (class reader_t *pipe_); void activated (class reader_t *pipe_);
void revive (class reader_t *pipe_); void terminated (class reader_t *pipe_);
void revive (class writer_t *pipe_);
// i_writer_events interface implementation.
void activated (class writer_t *pipe_);
void terminated (class writer_t *pipe_);
private: private:
~session_t (); ~session_t ();
// Define the delayed termination. (I.e. termination is postponed
// till all the data is flushed to the kernel.)
bool is_terminable ();
// Handlers for incoming commands. // Handlers for incoming commands.
void process_plug (); void process_plug ();
void process_unplug (); void process_unplug ();
......
This diff is collapsed.
...@@ -26,13 +26,13 @@ ...@@ -26,13 +26,13 @@
#include "../include/zmq.h" #include "../include/zmq.h"
#include "i_endpoint.hpp"
#include "object.hpp" #include "object.hpp"
#include "yarray_item.hpp" #include "yarray_item.hpp"
#include "mutex.hpp" #include "mutex.hpp"
#include "options.hpp" #include "options.hpp"
#include "stdint.hpp" #include "stdint.hpp"
#include "atomic_counter.hpp" #include "atomic_counter.hpp"
#include "signaler.hpp"
#include "stdint.hpp" #include "stdint.hpp"
#include "blob.hpp" #include "blob.hpp"
...@@ -40,11 +40,21 @@ namespace zmq ...@@ -40,11 +40,21 @@ namespace zmq
{ {
class socket_base_t : class socket_base_t :
public object_t, public i_endpoint, public yarray_item_t public object_t,
public yarray_item_t
{ {
public: public:
socket_base_t (class app_thread_t *parent_); // Create a socket of a specified type.
static socket_base_t *create (int type_, class ctx_t *parent_,
uint32_t slot_);
// Returns the signaler associated with this socket.
signaler_t *get_signaler ();
// Interrupt blocking call if the socket is stuck in one.
// This function can be called from a different thread!
void stop ();
// Interface for communication with the API layer. // Interface for communication with the API layer.
int setsockopt (int option_, const void *optval_, size_t optvallen_); int setsockopt (int option_, const void *optval_, size_t optvallen_);
...@@ -60,11 +70,6 @@ namespace zmq ...@@ -60,11 +70,6 @@ namespace zmq
// before the command is delivered. // before the command is delivered.
void inc_seqnum (); void inc_seqnum ();
// This function is used by the polling mechanism to determine
// whether the socket belongs to the application thread the poll
// is called from.
class app_thread_t *get_thread ();
// These functions are used by the polling mechanism to determine // These functions are used by the polling mechanism to determine
// which events are to be reported from this socket. // which events are to be reported from this socket.
bool has_in (); bool has_in ();
...@@ -85,43 +90,67 @@ namespace zmq ...@@ -85,43 +90,67 @@ namespace zmq
void unregister_session (uint64_t ordinal_); void unregister_session (uint64_t ordinal_);
class session_t *find_session (uint64_t ordinal_); class session_t *find_session (uint64_t ordinal_);
// i_endpoint interface implementation. // i_reader_events interface implementation.
void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void activated (class reader_t *pipe_);
const blob_t &peer_identity_); void terminated (class reader_t *pipe_);
void detach_inpipe (class reader_t *pipe_);
void detach_outpipe (class writer_t *pipe_); // i_writer_events interface implementation.
void kill (class reader_t *pipe_); void activated (class writer_t *pipe_);
void revive (class reader_t *pipe_); void terminated (class writer_t *pipe_);
void revive (class writer_t *pipe_);
// This function should be called only on zombie sockets. It tries
// to deallocate the zombie and returns true is successful.
bool dezombify ();
protected: protected:
// Destructor is protected. Socket is closed using 'close' function. socket_base_t (class ctx_t *parent_, uint32_t slot_);
virtual ~socket_base_t (); virtual ~socket_base_t ();
// Pipe management is done by individual socket types. // Concrete algorithms for the x- methods are to be defined by
// individual socket types.
virtual void xattach_pipes (class reader_t *inpipe_, virtual void xattach_pipes (class reader_t *inpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) = 0; class writer_t *outpipe_, const blob_t &peer_identity_) = 0;
virtual void xdetach_inpipe (class reader_t *pipe_) = 0; virtual void xterm_pipes () = 0;
virtual void xdetach_outpipe (class writer_t *pipe_) = 0; virtual bool xhas_pipes () = 0;
virtual void xkill (class reader_t *pipe_) = 0;
virtual void xrevive (class reader_t *pipe_) = 0;
virtual void xrevive (class writer_t *pipe_) = 0;
// Actual algorithms are to be defined by individual socket types. // The default implementation assumes there are no specific socket
// options for the particular socket type. If not so, overload this
// method.
virtual int xsetsockopt (int option_, const void *optval_, virtual int xsetsockopt (int option_, const void *optval_,
size_t optvallen_) = 0; size_t optvallen_);
virtual int xsend (zmq_msg_t *msg_, int options_) = 0;
virtual int xrecv (zmq_msg_t *msg_, int options_) = 0; // The default implementation assumes that send is not supported.
virtual bool xhas_in () = 0; virtual bool xhas_out ();
virtual bool xhas_out () = 0; virtual int xsend (zmq_msg_t *msg_, int options_);
// The default implementation assumes that recv in not supported.
virtual bool xhas_in ();
virtual int xrecv (zmq_msg_t *msg_, int options_);
// Socket options. // Socket options.
options_t options; options_t options;
// If true, socket was already closed but not yet deallocated
// because either shutdown is in process or there are still pipes
// attached to the socket.
bool zombie;
private: private:
// If no identity set generate one and call xattach_pipes ().
void attach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_);
// Processes commands sent to this socket (if any). If 'block' is
// set to true, returns only after at least one command was processed.
// If throttle argument is true, commands are processed at most once
// in a predefined time period.
void process_commands (bool block_, bool throttle_);
// Handlers for incoming commands. // Handlers for incoming commands.
void process_stop ();
void process_own (class owned_t *object_); void process_own (class owned_t *object_);
void process_bind (class reader_t *in_pipe_, class writer_t *out_pipe_, void process_bind (class reader_t *in_pipe_, class writer_t *out_pipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
...@@ -129,6 +158,12 @@ namespace zmq ...@@ -129,6 +158,12 @@ namespace zmq
void process_term_ack (); void process_term_ack ();
void process_seqnum (); void process_seqnum ();
// App thread's signaler object.
signaler_t signaler;
// Timestamp of when commands were processed the last time.
uint64_t last_processing_time;
// List of all I/O objects owned by this socket. The socket is // List of all I/O objects owned by this socket. The socket is
// responsible for deallocating them before it quits. // responsible for deallocating them before it quits.
typedef std::set <class owned_t*> io_objects_t; typedef std::set <class owned_t*> io_objects_t;
...@@ -144,13 +179,6 @@ namespace zmq ...@@ -144,13 +179,6 @@ namespace zmq
// If true there's a half-read message in the socket. // If true there's a half-read message in the socket.
bool rcvmore; bool rcvmore;
// Application thread the socket lives in.
class app_thread_t *app_thread;
// If true, socket is already shutting down. No new work should be
// started.
bool shutting_down;
// Sequence number of the last command sent to this object. // Sequence number of the last command sent to this object.
atomic_counter_t sent_seqnum; atomic_counter_t sent_seqnum;
......
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
#include "sub.hpp" #include "sub.hpp"
#include "err.hpp" #include "err.hpp"
zmq::sub_t::sub_t (class app_thread_t *parent_) : zmq::sub_t::sub_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
has_message (false), has_message (false),
more (false) more (false)
{ {
...@@ -46,31 +46,14 @@ void zmq::sub_t::xattach_pipes (class reader_t *inpipe_, ...@@ -46,31 +46,14 @@ void zmq::sub_t::xattach_pipes (class reader_t *inpipe_,
fq.attach (inpipe_); fq.attach (inpipe_);
} }
void zmq::sub_t::xdetach_inpipe (class reader_t *pipe_) void zmq::sub_t::xterm_pipes ()
{ {
zmq_assert (pipe_); fq.term_pipes ();
fq.detach (pipe_);
} }
void zmq::sub_t::xdetach_outpipe (class writer_t *pipe_) bool zmq::sub_t::xhas_pipes ()
{ {
// SUB socket is read-only thus there should be no outpipes. return fq.has_pipes ();
zmq_assert (false);
}
void zmq::sub_t::xkill (class reader_t *pipe_)
{
fq.kill (pipe_);
}
void zmq::sub_t::xrevive (class reader_t *pipe_)
{
fq.revive (pipe_);
}
void zmq::sub_t::xrevive (class writer_t *pipe_)
{
zmq_assert (false);
} }
int zmq::sub_t::xsetsockopt (int option_, const void *optval_, int zmq::sub_t::xsetsockopt (int option_, const void *optval_,
...@@ -93,12 +76,6 @@ int zmq::sub_t::xsetsockopt (int option_, const void *optval_, ...@@ -93,12 +76,6 @@ int zmq::sub_t::xsetsockopt (int option_, const void *optval_,
return -1; return -1;
} }
int zmq::sub_t::xsend (zmq_msg_t *msg_, int flags_)
{
errno = ENOTSUP;
return -1;
}
int zmq::sub_t::xrecv (zmq_msg_t *msg_, int flags_) int zmq::sub_t::xrecv (zmq_msg_t *msg_, int flags_)
{ {
// If there's already a message prepared by a previous call to zmq_poll, // If there's already a message prepared by a previous call to zmq_poll,
...@@ -179,11 +156,6 @@ bool zmq::sub_t::xhas_in () ...@@ -179,11 +156,6 @@ bool zmq::sub_t::xhas_in ()
} }
} }
bool zmq::sub_t::xhas_out ()
{
return false;
}
bool zmq::sub_t::match (zmq_msg_t *msg_) bool zmq::sub_t::match (zmq_msg_t *msg_)
{ {
return subscriptions.check ((unsigned char*) zmq_msg_data (msg_), return subscriptions.check ((unsigned char*) zmq_msg_data (msg_),
......
...@@ -33,7 +33,7 @@ namespace zmq ...@@ -33,7 +33,7 @@ namespace zmq
{ {
public: public:
sub_t (class app_thread_t *parent_); sub_t (class ctx_t *parent_, uint32_t slot_);
~sub_t (); ~sub_t ();
protected: protected:
...@@ -41,16 +41,11 @@ namespace zmq ...@@ -41,16 +41,11 @@ namespace zmq
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_); int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out ();
private: private:
......
...@@ -38,16 +38,6 @@ void zmq::thread_t::stop () ...@@ -38,16 +38,6 @@ void zmq::thread_t::stop ()
win_assert (rc != WAIT_FAILED); win_assert (rc != WAIT_FAILED);
} }
zmq::thread_t::id_t zmq::thread_t::id ()
{
return GetCurrentThreadId ();
}
bool zmq::thread_t::equal (id_t id1_, id_t id2_)
{
return id1_ == id2_;
}
unsigned int __stdcall zmq::thread_t::thread_routine (void *arg_) unsigned int __stdcall zmq::thread_t::thread_routine (void *arg_)
{ {
thread_t *self = (thread_t*) arg_; thread_t *self = (thread_t*) arg_;
...@@ -73,16 +63,6 @@ void zmq::thread_t::stop () ...@@ -73,16 +63,6 @@ void zmq::thread_t::stop ()
errno_assert (rc == 0); errno_assert (rc == 0);
} }
zmq::thread_t::id_t zmq::thread_t::id ()
{
return pthread_self ();
}
bool zmq::thread_t::equal (id_t id1_, id_t id2_)
{
return pthread_equal (id1_, id2_) != 0;
}
void *zmq::thread_t::thread_routine (void *arg_) void *zmq::thread_t::thread_routine (void *arg_)
{ {
#if !defined ZMQ_HAVE_OPENVMS #if !defined ZMQ_HAVE_OPENVMS
......
...@@ -55,15 +55,6 @@ namespace zmq ...@@ -55,15 +55,6 @@ namespace zmq
// Waits for thread termination. // Waits for thread termination.
void stop (); void stop ();
#ifdef ZMQ_HAVE_WINDOWS
typedef DWORD id_t;
#else
typedef pthread_t id_t;
#endif
static id_t id ();
static bool equal (id_t id1_, id_t id2_);
private: private:
#ifdef ZMQ_HAVE_WINDOWS #ifdef ZMQ_HAVE_WINDOWS
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include "err.hpp" #include "err.hpp"
#include "pipe.hpp" #include "pipe.hpp"
zmq::xrep_t::xrep_t (class app_thread_t *parent_) : zmq::xrep_t::xrep_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_), socket_base_t (parent_, slot_),
current_in (0), current_in (0),
prefetched (false), prefetched (false),
more_in (false), more_in (false),
...@@ -41,31 +41,41 @@ zmq::xrep_t::xrep_t (class app_thread_t *parent_) : ...@@ -41,31 +41,41 @@ zmq::xrep_t::xrep_t (class app_thread_t *parent_) :
zmq::xrep_t::~xrep_t () zmq::xrep_t::~xrep_t ()
{ {
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); it++) zmq_assert (inpipes.empty ());
it->reader->term (); zmq_assert (outpipes.empty ());
for (outpipes_t::iterator it = outpipes.begin (); it != outpipes.end ();
it++)
it->second.writer->term ();
} }
void zmq::xrep_t::xattach_pipes (class reader_t *inpipe_, void zmq::xrep_t::xattach_pipes (reader_t *inpipe_, writer_t *outpipe_,
class writer_t *outpipe_, const blob_t &peer_identity_) const blob_t &peer_identity_)
{ {
zmq_assert (inpipe_ && outpipe_); zmq_assert (inpipe_ && outpipe_);
outpipe_->set_event_sink (this);
// TODO: What if new connection has same peer identity as the old one? // TODO: What if new connection has same peer identity as the old one?
outpipe_t outpipe = {outpipe_, true}; outpipe_t outpipe = {outpipe_, true};
bool ok = outpipes.insert (std::make_pair ( bool ok = outpipes.insert (std::make_pair (
peer_identity_, outpipe)).second; peer_identity_, outpipe)).second;
zmq_assert (ok); zmq_assert (ok);
inpipe_->set_event_sink (this);
inpipe_t inpipe = {inpipe_, peer_identity_, true}; inpipe_t inpipe = {inpipe_, peer_identity_, true};
inpipes.push_back (inpipe); inpipes.push_back (inpipe);
} }
void zmq::xrep_t::xdetach_inpipe (class reader_t *pipe_) void zmq::xrep_t::xterm_pipes ()
{
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end ();
it++)
it->reader->terminate ();
for (outpipes_t::iterator it = outpipes.begin (); it != outpipes.end ();
it++)
it->second.writer->terminate ();
}
void zmq::xrep_t::terminated (reader_t *pipe_)
{ {
// TODO:!
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end ();
it++) { it++) {
if (it->reader == pipe_) { if (it->reader == pipe_) {
...@@ -76,7 +86,7 @@ void zmq::xrep_t::xdetach_inpipe (class reader_t *pipe_) ...@@ -76,7 +86,7 @@ void zmq::xrep_t::xdetach_inpipe (class reader_t *pipe_)
zmq_assert (false); zmq_assert (false);
} }
void zmq::xrep_t::xdetach_outpipe (class writer_t *pipe_) void zmq::xrep_t::terminated (writer_t *pipe_)
{ {
for (outpipes_t::iterator it = outpipes.begin (); for (outpipes_t::iterator it = outpipes.begin ();
it != outpipes.end (); ++it) { it != outpipes.end (); ++it) {
...@@ -90,20 +100,12 @@ void zmq::xrep_t::xdetach_outpipe (class writer_t *pipe_) ...@@ -90,20 +100,12 @@ void zmq::xrep_t::xdetach_outpipe (class writer_t *pipe_)
zmq_assert (false); zmq_assert (false);
} }
void zmq::xrep_t::xkill (class reader_t *pipe_) bool zmq::xrep_t::xhas_pipes ()
{ {
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); return !inpipes.empty () || !outpipes.empty ();
it++) {
if (it->reader == pipe_) {
zmq_assert (it->active);
it->active = false;
return;
}
}
zmq_assert (false);
} }
void zmq::xrep_t::xrevive (class reader_t *pipe_) void zmq::xrep_t::activated (reader_t *pipe_)
{ {
for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end (); for (inpipes_t::iterator it = inpipes.begin (); it != inpipes.end ();
it++) { it++) {
...@@ -116,7 +118,7 @@ void zmq::xrep_t::xrevive (class reader_t *pipe_) ...@@ -116,7 +118,7 @@ void zmq::xrep_t::xrevive (class reader_t *pipe_)
zmq_assert (false); zmq_assert (false);
} }
void zmq::xrep_t::xrevive (class writer_t *pipe_) void zmq::xrep_t::activated (writer_t *pipe_)
{ {
for (outpipes_t::iterator it = outpipes.begin (); for (outpipes_t::iterator it = outpipes.begin ();
it != outpipes.end (); ++it) { it != outpipes.end (); ++it) {
...@@ -129,13 +131,6 @@ void zmq::xrep_t::xrevive (class writer_t *pipe_) ...@@ -129,13 +131,6 @@ void zmq::xrep_t::xrevive (class writer_t *pipe_)
zmq_assert (false); zmq_assert (false);
} }
int zmq::xrep_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{
errno = EINVAL;
return -1;
}
int zmq::xrep_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::xrep_t::xsend (zmq_msg_t *msg_, int flags_)
{ {
// If this is the first part of the message it's the identity of the // If this is the first part of the message it's the identity of the
...@@ -232,7 +227,9 @@ int zmq::xrep_t::xrecv (zmq_msg_t *msg_, int flags_) ...@@ -232,7 +227,9 @@ int zmq::xrep_t::xrecv (zmq_msg_t *msg_, int flags_)
return 0; return 0;
} }
// If me don't have a message, move to next pipe. // If me don't have a message, mark the pipe as passive and
// move to next pipe.
inpipes [current_in].active = false;
current_in++; current_in++;
if (current_in >= inpipes.size ()) if (current_in >= inpipes.size ())
current_in = 0; current_in = 0;
...@@ -259,6 +256,10 @@ bool zmq::xrep_t::xhas_in () ...@@ -259,6 +256,10 @@ bool zmq::xrep_t::xhas_in ()
if (inpipes [current_in].active && if (inpipes [current_in].active &&
inpipes [current_in].reader->check_read ()) inpipes [current_in].reader->check_read ())
return true; return true;
// If me don't have a message, mark the pipe as passive and
// move to next pipe.
inpipes [current_in].active = false;
current_in++; current_in++;
if (current_in >= inpipes.size ()) if (current_in >= inpipes.size ())
current_in = 0; current_in = 0;
......
...@@ -25,32 +25,40 @@ ...@@ -25,32 +25,40 @@
#include "socket_base.hpp" #include "socket_base.hpp"
#include "blob.hpp" #include "blob.hpp"
#include "pipe.hpp"
namespace zmq namespace zmq
{ {
// TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm. // TODO: This class uses O(n) scheduling. Rewrite it to use O(1) algorithm.
class xrep_t : public socket_base_t class xrep_t :
public socket_base_t,
public i_reader_events,
public i_writer_events
{ {
public: public:
xrep_t (class app_thread_t *parent_); xrep_t (class ctx_t *parent_, uint32_t slot_);
~xrep_t (); ~xrep_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (reader_t *inpipe_, writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
bool xhas_out (); bool xhas_out ();
// i_reader_events interface implementation.
void activated (reader_t *pipe_);
void terminated (reader_t *pipe_);
// i_writer_events interface implementation.
void activated (writer_t *pipe_);
void terminated (writer_t *pipe_);
private: private:
struct inpipe_t struct inpipe_t
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#include "xreq.hpp" #include "xreq.hpp"
#include "err.hpp" #include "err.hpp"
zmq::xreq_t::xreq_t (class app_thread_t *parent_) : zmq::xreq_t::xreq_t (class ctx_t *parent_, uint32_t slot_) :
socket_base_t (parent_) socket_base_t (parent_, slot_)
{ {
options.requires_in = true; options.requires_in = true;
options.requires_out = true; options.requires_out = true;
...@@ -41,38 +41,15 @@ void zmq::xreq_t::xattach_pipes (class reader_t *inpipe_, ...@@ -41,38 +41,15 @@ void zmq::xreq_t::xattach_pipes (class reader_t *inpipe_,
lb.attach (outpipe_); lb.attach (outpipe_);
} }
void zmq::xreq_t::xdetach_inpipe (class reader_t *pipe_) void zmq::xreq_t::xterm_pipes ()
{ {
zmq_assert (pipe_); fq.term_pipes ();
fq.detach (pipe_); lb.term_pipes ();
} }
void zmq::xreq_t::xdetach_outpipe (class writer_t *pipe_) bool zmq::xreq_t::xhas_pipes ()
{ {
zmq_assert (pipe_); return fq.has_pipes () || lb.has_pipes ();
lb.detach (pipe_);
}
void zmq::xreq_t::xkill (class reader_t *pipe_)
{
fq.kill (pipe_);
}
void zmq::xreq_t::xrevive (class reader_t *pipe_)
{
fq.revive (pipe_);
}
void zmq::xreq_t::xrevive (class writer_t *pipe_)
{
lb.revive (pipe_);
}
int zmq::xreq_t::xsetsockopt (int option_, const void *optval_,
size_t optvallen_)
{
errno = EINVAL;
return -1;
} }
int zmq::xreq_t::xsend (zmq_msg_t *msg_, int flags_) int zmq::xreq_t::xsend (zmq_msg_t *msg_, int flags_)
......
...@@ -31,18 +31,14 @@ namespace zmq ...@@ -31,18 +31,14 @@ namespace zmq
{ {
public: public:
xreq_t (class app_thread_t *parent_); xreq_t (class ctx_t *parent_, uint32_t slot_);
~xreq_t (); ~xreq_t ();
// Overloads of functions from socket_base_t. // Overloads of functions from socket_base_t.
void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_, void xattach_pipes (class reader_t *inpipe_, class writer_t *outpipe_,
const blob_t &peer_identity_); const blob_t &peer_identity_);
void xdetach_inpipe (class reader_t *pipe_); void xterm_pipes ();
void xdetach_outpipe (class writer_t *pipe_); bool xhas_pipes ();
void xkill (class reader_t *pipe_);
void xrevive (class reader_t *pipe_);
void xrevive (class writer_t *pipe_);
int xsetsockopt (int option_, const void *optval_, size_t optvallen_);
int xsend (zmq_msg_t *msg_, int flags_); int xsend (zmq_msg_t *msg_, int flags_);
int xrecv (zmq_msg_t *msg_, int flags_); int xrecv (zmq_msg_t *msg_, int flags_);
bool xhas_in (); bool xhas_in ();
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include "queue.hpp" #include "queue.hpp"
#include "streamer.hpp" #include "streamer.hpp"
#include "socket_base.hpp" #include "socket_base.hpp"
#include "app_thread.hpp"
#include "msg_content.hpp" #include "msg_content.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "stdint.hpp" #include "stdint.hpp"
...@@ -83,8 +82,6 @@ const char *zmq_strerror (int errnum_) ...@@ -83,8 +82,6 @@ const char *zmq_strerror (int errnum_)
case EINPROGRESS: case EINPROGRESS:
return "Operation in progress"; return "Operation in progress";
#endif #endif
case EMTHREAD:
return "Number of preallocated application threads exceeded";
case EFSM: case EFSM:
return "Operation cannot be accomplished in current state"; return "Operation cannot be accomplished in current state";
case ENOCOMPATPROTO: case ENOCOMPATPROTO:
...@@ -367,6 +364,7 @@ int zmq_recv (void *s_, zmq_msg_t *msg_, int flags_) ...@@ -367,6 +364,7 @@ int zmq_recv (void *s_, zmq_msg_t *msg_, int flags_)
int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_)
{ {
/*
#if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\ #if defined ZMQ_HAVE_LINUX || defined ZMQ_HAVE_FREEBSD ||\
defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\ defined ZMQ_HAVE_OPENBSD || defined ZMQ_HAVE_SOLARIS ||\
defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\ defined ZMQ_HAVE_OSX || defined ZMQ_HAVE_QNXNTO ||\
...@@ -679,6 +677,9 @@ int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_) ...@@ -679,6 +677,9 @@ int zmq_poll (zmq_pollitem_t *items_, int nitems_, long timeout_)
errno = ENOTSUP; errno = ENOTSUP;
return -1; return -1;
#endif #endif
*/
zmq_assert (false);
return -1;
} }
int zmq_errno () int zmq_errno ()
......
...@@ -52,7 +52,7 @@ bool zmq::zmq_encoder_t::size_ready () ...@@ -52,7 +52,7 @@ bool zmq::zmq_encoder_t::size_ready ()
bool zmq::zmq_encoder_t::message_ready () bool zmq::zmq_encoder_t::message_ready ()
{ {
// Destroy content of the old message. // Destroy content of the old message.
zmq_msg_close(&in_progress); zmq_msg_close (&in_progress);
// Read new message. If there is none, return false. // Read new message. If there is none, return false.
// Note that new state is set only if write is successful. That way // Note that new state is set only if write is successful. That way
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment