diff options
author | William R. Otte <wotte@dre.vanderbilt.edu> | 2006-07-24 15:50:30 +0000 |
---|---|---|
committer | William R. Otte <wotte@dre.vanderbilt.edu> | 2006-07-24 15:50:30 +0000 |
commit | 7a52d43a162b23d9e85e7b955e9b2c8e9caf550e (patch) | |
tree | 66a84b20d47f2269d8bdc6e0323f338763424d3a /ACE/examples/Reactor | |
parent | 0e49389337be86641451a5c36c24bf742fe97523 (diff) | |
download | ATCD-7a52d43a162b23d9e85e7b955e9b2c8e9caf550e.tar.gz |
Repo restructuring
Diffstat (limited to 'ACE/examples/Reactor')
90 files changed, 13969 insertions, 0 deletions
diff --git a/ACE/examples/Reactor/Dgram/.cvsignore b/ACE/examples/Reactor/Dgram/.cvsignore new file mode 100644 index 00000000000..94126b14c4e --- /dev/null +++ b/ACE/examples/Reactor/Dgram/.cvsignore @@ -0,0 +1,4 @@ +codgram +codgram +dgram +dgram diff --git a/ACE/examples/Reactor/Dgram/CODgram.cpp b/ACE/examples/Reactor/Dgram/CODgram.cpp new file mode 100644 index 00000000000..f423c43fb8c --- /dev/null +++ b/ACE/examples/Reactor/Dgram/CODgram.cpp @@ -0,0 +1,254 @@ +// $Id$ + +// Exercise the <ACE_SOCK_CODgram> wrapper along with the +// <ACE_Reactor>. This test simply ping-pongs datagrams back and +// forth between the peer1 and peer2 processes. This test can +// be run in two ways: +// +// 1. Stand-alone -- e.g., +// +// % ./CODgram +// +// which will spawn a child process and run peer1 and peer2 +// in different processes on the same machine. +// +// 2. Distributed -- e.g., +// +// # Peer1 +// % ./CODgram 10002 tango.cs.wustl.edu 10003 peer1 +// +// # Peer1 +// % ./CODgram 10003 tango.cs.wustl.edu 10002 peer2 +// +// which will run peer1 and peer2 in different processes +// on the same or different machines. Note that you MUST +// give the name "peer1" as the final argument to one and +// only one of the programs so that the test will work properly. + +#include "ace/OS_main.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Reactor.h" +#include "ace/SOCK_CODgram.h" +#include "ace/INET_Addr.h" +#include "ace/Process.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(Dgram, CODgram, "$Id$") + +// Port used to receive for dgrams. +static u_short port1; + +class Dgram_Endpoint : public ACE_Event_Handler +{ +public: + Dgram_Endpoint (const ACE_INET_Addr &remote_addr, + const ACE_INET_Addr &local_addr); + + // = Hook methods inherited from the <ACE_Event_Handler>. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_timeout (const ACE_Time_Value & tv, + const void *arg = 0); + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + int send (const char *buf, size_t len); + // Send the <buf> to the peer. + +private: + ACE_SOCK_CODgram endpoint_; + // Wrapper for sending/receiving dgrams. +}; + +int +Dgram_Endpoint::send (const char *buf, size_t len) +{ + return this->endpoint_.send (buf, len); +} + +int +Dgram_Endpoint::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_UNUSED_ARG (handle); + + this->endpoint_.close (); + return 0; +} + +Dgram_Endpoint::Dgram_Endpoint (const ACE_INET_Addr &remote_addr, + const ACE_INET_Addr &local_addr) + : endpoint_ (remote_addr, local_addr) +{ +} + +ACE_HANDLE +Dgram_Endpoint::get_handle (void) const +{ + return this->endpoint_.get_handle (); +} + +int +Dgram_Endpoint::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) activity occurred on handle %d!\n", + this->endpoint_.get_handle ())); + + ssize_t n = this->endpoint_.recv (buf, sizeof buf); + + if (n == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "handle_input")); + else + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) buf of size %d = %*s\n", + n, n, buf)); + return 0; +} + +int +Dgram_Endpoint::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) timed out for endpoint\n")); + return 0; +} + +static int +run_test (u_short localport, + const ACE_TCHAR *remotehost, + u_short remoteport, + const ACE_TCHAR *peer) +{ + ACE_INET_Addr remote_addr (remoteport, + remotehost); + ACE_INET_Addr local_addr (localport); + + Dgram_Endpoint endpoint (remote_addr, local_addr); + + // Read data from other side. + if (ACE_Reactor::instance ()->register_handler + (&endpoint, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::register_handler"), + -1); + char buf[BUFSIZ]; + ACE_OS::strcpy (buf, + "Data to transmit"); + size_t len = ACE_OS::strlen (buf); + + // "peer1" is the "initiator." + if (ACE_OS::strncmp (peer, ACE_TEXT("peer1"), 5) == 0) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) sending data\n")); + for (size_t i = 0; i < 20; i++) + { + endpoint.send (buf, len); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) .\n")); + ACE_OS::sleep (1); + } + } + + for (int i = 0; i < 40; i++) + { + // Wait up to 10 seconds for data. + ACE_Time_Value tv (10, 0); + + if (ACE_Reactor::instance ()->handle_events (tv) <= 0) + ACE_ERROR_RETURN ((LM_DEBUG, + "(%P|%t) %p\n", + "handle_events"), + -1); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) return from handle events\n")); + + endpoint.send (buf, len); + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) .\n")); + } + + if (ACE_Reactor::instance ()->remove_handler + (&endpoint, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler"), + -1); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) exiting\n")); + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + // Estabish call backs and socket names. + + port1 = argc > 1 ? ACE_OS::atoi (argv[1]) : ACE_DEFAULT_SERVER_PORT; + const ACE_TCHAR *remotehost = argc > 2 ? argv[2] : ACE_DEFAULT_SERVER_HOST; + const u_short port2 = argc > 3 ? ACE_OS::atoi (argv[3]) : port1 + 1; + + // Providing the fourth command line argument indicates we don't + // want to spawn a new process. On Win32, we use this to exec the + // new program. + if (argc > 4) + run_test (port1, + remotehost, + port2, + argv[4]); + else + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) local port = %d, remote host = %s, remote port = %d\n", + port1, + remotehost, + port2)); + + ACE_Process_Options options; + options.command_line ("%s %d %s %d %c", + argv[0], + port1, + remotehost, + port2, + 'c'); + + // This has no effect on NT and will spawn a process that exec + // the above run_test function. + options.creation_flags (ACE_Process_Options::NO_EXEC); + + ACE_Process new_process; + + switch (new_process.spawn (options)) + { + case -1: + return -1; + + case 0: + run_test (port1, + remotehost, + port2, + ACE_TEXT("peer1")); + break; + + default: + run_test (port2, + remotehost, + port1, + ACE_TEXT("peer2")); + new_process.wait (); + break; + } + } + + return 0; +} diff --git a/ACE/examples/Reactor/Dgram/Dgram.cpp b/ACE/examples/Reactor/Dgram/Dgram.cpp new file mode 100644 index 00000000000..c4c21e84186 --- /dev/null +++ b/ACE/examples/Reactor/Dgram/Dgram.cpp @@ -0,0 +1,258 @@ +// $Id$ + +// Exercise the <ACE_SOCK_Dgram> wrapper along with the <ACE_Reactor>. +// This test simply ping-pongs datagrams back and forth between the +// peer1 and peer2 processes. This test can be run in two ways: +// +// 1. Stand-alone -- e.g., +// +// % ./Dgram +// +// which will spawn a child process and run peer1 and peer2 +// in different processes on the same machine. +// +// 2. Distributed -- e.g., +// +// # Peer1 +// % ./Dgram 10002 tango.cs.wustl.edu 10003 peer1 +// +// # Peer1 +// % ./Dgram 10003 tango.cs.wustl.edu 10002 peer2 +// +// which will run peer1 and peer2 in different processes +// on the same or different machines. Note that you MUST +// give the name "peer1" as the final argument to one and +// only one of the programs so that the test will work properly. + +#include "ace/OS_main.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/Reactor.h" +#include "ace/Process.h" +#include "ace/SOCK_Dgram.h" +#include "ace/INET_Addr.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(Dgram, Dgram, "$Id$") + +// Port used to receive for dgrams. +static u_short port1; + +class Dgram_Endpoint : public ACE_Event_Handler +{ +public: + Dgram_Endpoint (const ACE_INET_Addr &local_addr); + + // = Hook methods inherited from the <ACE_Event_Handler>. + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_timeout (const ACE_Time_Value & tv, + const void *arg = 0); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + int send (const char *buf, size_t len, const ACE_INET_Addr &); + // Send the <buf> to the peer. + +private: + ACE_SOCK_Dgram endpoint_; + // Wrapper for sending/receiving dgrams. +}; + +int +Dgram_Endpoint::send (const char *buf, + size_t len, + const ACE_INET_Addr &addr) +{ + return this->endpoint_.send (buf, len, addr); +} + +Dgram_Endpoint::Dgram_Endpoint (const ACE_INET_Addr &local_addr) + : endpoint_ (local_addr) +{ +} + +ACE_HANDLE +Dgram_Endpoint::get_handle (void) const +{ + return this->endpoint_.get_handle (); +} + +int +Dgram_Endpoint::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_UNUSED_ARG (handle); + + this->endpoint_.close (); + delete this; + return 0; +} + +int +Dgram_Endpoint::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + ACE_INET_Addr from_addr; + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) activity occurred on handle %d!\n", + this->endpoint_.get_handle ())); + + ssize_t n = this->endpoint_.recv (buf, + sizeof buf, + from_addr); + + if (n == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "handle_input")); + else + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) buf of size %d = %*s\n", + n, + n, + buf)); + return 0; +} + +int +Dgram_Endpoint::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) timed out for endpoint\n")); + return 0; +} + +static int +run_test (u_short localport, + const ACE_TCHAR *remotehost, + u_short remoteport, + const ACE_TCHAR *peer) +{ + ACE_INET_Addr remote_addr (remoteport, + remotehost); + ACE_INET_Addr local_addr (localport); + + Dgram_Endpoint *endpoint; + + ACE_NEW_RETURN (endpoint, + Dgram_Endpoint (local_addr), + -1); + + // Read data from other side. + if (ACE_Reactor::instance ()->register_handler + (endpoint, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::register_handler"), + -1); + + char buf[BUFSIZ]; + ACE_OS::strcpy (buf, "Data to transmit"); + size_t len = ACE_OS::strlen (buf); + + if (ACE_OS::strncmp (peer, ACE_TEXT("peer1"), 5) == 0) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) sending data\n")); + + for (size_t i = 0; i < 20; i++) + { + endpoint->send (buf, len, remote_addr); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) .\n")); + ACE_OS::sleep (1); + } + } + + for (int i = 0; i < 40; i++) + { + // Wait up to 10 seconds for data. + ACE_Time_Value tv (10, 0); + + if (ACE_Reactor::instance ()->handle_events (tv) <= 0) + ACE_ERROR_RETURN ((LM_DEBUG, + "(%P|%t) %p\n", + "handle_events"), + -1); + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) return from handle events\n")); + + endpoint->send (buf, len, remote_addr); + + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) .\n")); + } + + if (ACE_Reactor::instance ()->remove_handler + (endpoint, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE_Reactor::remove_handler"), + -1); + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) exiting\n")); + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + // Estabish call backs and socket names. + + port1 = argc > 1 ? ACE_OS::atoi (argv[1]) : ACE_DEFAULT_SERVER_PORT; + const ACE_TCHAR *remotehost = argc > 2 ? argv[2] : ACE_DEFAULT_SERVER_HOST; + const u_short port2 = argc > 3 ? ACE_OS::atoi (argv[3]) : port1 + 1; + + // Providing the fourth command line argument indicate we don't want + // to spawn a new process. On Win32, we use this to exec the new + // program. + if (argc > 4) + run_test (port1, remotehost, port2, argv[4]); + else + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t) local port = %d, remote host = %s, remote port = %d\n", + port1, + remotehost, + port2)); + + ACE_Process_Options options; + options.command_line ("%s %d %s %d %c", + argv[0], + port1, + remotehost, + port2, + 'c'); + + // This has no effect on NT and will spawn a process that exec + // the above run_test function. + options.creation_flags (ACE_Process_Options::NO_EXEC); + + ACE_Process new_process; + switch (new_process.spawn (options)) + { + case -1: + return -1; + + case 0: + run_test (port1, + remotehost, + port2, + ACE_TEXT("peer1")); + break; + + default: + run_test (port2, + remotehost, + port1, + ACE_TEXT("peer2")); + new_process.wait (); + break; + } + } + return 0; +} diff --git a/ACE/examples/Reactor/Dgram/Makefile.am b/ACE/examples/Reactor/Dgram/Makefile.am new file mode 100644 index 00000000000..797f4b3d75e --- /dev/null +++ b/ACE/examples/Reactor/Dgram/Makefile.am @@ -0,0 +1,51 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + + +## Makefile.Reactor_Dgram.am +noinst_PROGRAMS = dgram + +dgram_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +dgram_SOURCES = \ + Dgram.cpp + +dgram_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Dgram_CO.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += codgram + +codgram_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +codgram_SOURCES = \ + CODgram.cpp + +codgram_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/Dgram/Reactor_Dgram.mpc b/ACE/examples/Reactor/Dgram/Reactor_Dgram.mpc new file mode 100644 index 00000000000..1040aedc184 --- /dev/null +++ b/ACE/examples/Reactor/Dgram/Reactor_Dgram.mpc @@ -0,0 +1,18 @@ +// -*- MPC -*- +// $Id$ + +project(*CO) : aceexe { + avoids += ace_for_tao + exename = codgram + Source_Files { + CODgram.cpp + } +} + +project : aceexe { + exename = dgram + Source_Files { + Dgram.cpp + } +} + diff --git a/ACE/examples/Reactor/FIFO/.cvsignore b/ACE/examples/Reactor/FIFO/.cvsignore new file mode 100644 index 00000000000..955ffdc75d5 --- /dev/null +++ b/ACE/examples/Reactor/FIFO/.cvsignore @@ -0,0 +1,4 @@ +client +client +server +server diff --git a/ACE/examples/Reactor/FIFO/Makefile.am b/ACE/examples/Reactor/FIFO/Makefile.am new file mode 100644 index 00000000000..5540e860968 --- /dev/null +++ b/ACE/examples/Reactor/FIFO/Makefile.am @@ -0,0 +1,56 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.Reactor_FIFO_Client.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += client + +client_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +client_SOURCES = \ + client.cpp + +client_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Reactor_FIFO_Server.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += server + +server_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +server_SOURCES = \ + server.cpp + +server_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/FIFO/Reactor_FIFO.mpc b/ACE/examples/Reactor/FIFO/Reactor_FIFO.mpc new file mode 100644 index 00000000000..072ec5412b8 --- /dev/null +++ b/ACE/examples/Reactor/FIFO/Reactor_FIFO.mpc @@ -0,0 +1,18 @@ +// -*- MPC -*- +// $Id$ + +project(*client) : aceexe { + avoids += ace_for_tao + exename = client + Source_Files { + client.cpp + } +} + +project(*server) : aceexe { + avoids += ace_for_tao + exename = server + Source_Files { + server.cpp + } +} diff --git a/ACE/examples/Reactor/FIFO/client.cpp b/ACE/examples/Reactor/FIFO/client.cpp new file mode 100644 index 00000000000..daa8d3304ad --- /dev/null +++ b/ACE/examples/Reactor/FIFO/client.cpp @@ -0,0 +1,23 @@ +// $Id$ + +#include "ace/FIFO_Send_Msg.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_stropts.h" + +ACE_RCSID(FIFO, client, "$Id$") + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + char buf[] = "hello world"; + ACE_Str_Buf msg (buf, sizeof buf); + + ACE_FIFO_Send_Msg fifo_sender (ACE_DEFAULT_RENDEZVOUS, + O_WRONLY | O_CREAT, + ACE_DEFAULT_FILE_PERMS); + + if (fifo_sender.send (msg) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "send error for fifo"), -1); + else + return 0; +} diff --git a/ACE/examples/Reactor/FIFO/server.cpp b/ACE/examples/Reactor/FIFO/server.cpp new file mode 100644 index 00000000000..b6e91dd5046 --- /dev/null +++ b/ACE/examples/Reactor/FIFO/server.cpp @@ -0,0 +1,89 @@ +// $Id$ + +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Event_Handler.h" +#include "ace/FIFO_Recv_Msg.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_stropts.h" + +ACE_RCSID(FIFO, server, "$Id$") + +class FIFO_Recv_Handler : public ACE_Event_Handler +{ +public: + FIFO_Recv_Handler (void); + ~FIFO_Recv_Handler (void); + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE fd); + +private: + ACE_FIFO_Recv_Msg fifo_reader_; +}; + +FIFO_Recv_Handler::FIFO_Recv_Handler (void) +{ + ACE_OS::unlink (ACE_DEFAULT_RENDEZVOUS); + + // Make sure to open the FIFO with the "persistent" flag enabled + // (which is the default). + if (this->fifo_reader_.open (ACE_DEFAULT_RENDEZVOUS) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("open"))); + + // Register with the Reactor. + if (ACE_Reactor::instance ()->register_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("register_handler"))); +} + +ACE_HANDLE +FIFO_Recv_Handler::get_handle (void) const +{ + return this->fifo_reader_.get_handle (); +} + +FIFO_Recv_Handler::~FIFO_Recv_Handler (void) +{ + this->fifo_reader_.close (); + this->fifo_reader_.remove (); +} + +int +FIFO_Recv_Handler::handle_input (ACE_HANDLE) +{ + char buf[BUFSIZ]; + + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("handle_input\n"))); + + ACE_Str_Buf msg (buf, 0, sizeof buf); + + ssize_t n = this->fifo_reader_.recv (msg); + + if (n < 0) + ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("recv")), -1); + else + { + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("msg.len = %d, n = %d\n"), msg.len, n)); + + if (msg.len > 0) + { + // Do some work in here... + ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("msg.buf = %C\n"), msg.buf)); + } + return 0; + } +} + +int +ACE_TMAIN (int, ACE_TCHAR *argv[]) +{ + ACE_Service_Config daemon (argv[0]); + + FIFO_Recv_Handler fr_handler; + + ACE_Reactor::instance ()->run_reactor_event_loop (); + + return 0; +} diff --git a/ACE/examples/Reactor/Makefile.am b/ACE/examples/Reactor/Makefile.am new file mode 100644 index 00000000000..24d9217302d --- /dev/null +++ b/ACE/examples/Reactor/Makefile.am @@ -0,0 +1,20 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +SUBDIRS = \ + Dgram \ + FIFO \ + Misc \ + Multicast \ + Ntalker \ + Proactor \ + TP_Reactor \ + WFMO_Reactor + diff --git a/ACE/examples/Reactor/Misc/.cvsignore b/ACE/examples/Reactor/Misc/.cvsignore new file mode 100644 index 00000000000..f7cc1865efa --- /dev/null +++ b/ACE/examples/Reactor/Misc/.cvsignore @@ -0,0 +1,16 @@ +demuxing +demuxing +early_timeouts +early_timeouts +notification +notification +pingpong +pingpong +reactors +reactors +signals_1 +signals_1 +signals_2 +signals_2 +timer_queue +timer_queue diff --git a/ACE/examples/Reactor/Misc/Makefile.am b/ACE/examples/Reactor/Misc/Makefile.am new file mode 100644 index 00000000000..47111585f87 --- /dev/null +++ b/ACE/examples/Reactor/Misc/Makefile.am @@ -0,0 +1,137 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +## Makefile.Reactor_Misc_Demuxing.am +noinst_PROGRAMS = demuxing + +demuxing_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +demuxing_SOURCES = \ + test_demuxing.cpp + +demuxing_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Early_Timeouts.am +noinst_PROGRAMS += early_timeouts + +early_timeouts_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +early_timeouts_SOURCES = \ + test_early_timeouts.cpp + +early_timeouts_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Event_Handler_t.am +noinst_PROGRAMS += event_handler_t + +event_handler_t_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +event_handler_t_SOURCES = \ + test_event_handler_t.cpp + +event_handler_t_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Notification.am +noinst_PROGRAMS += notification + +notification_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +notification_SOURCES = \ + notification.cpp + +notification_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Pingpong.am +noinst_PROGRAMS += pingpong + +pingpong_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +pingpong_SOURCES = \ + pingpong.cpp + +pingpong_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Reactors.am +noinst_PROGRAMS += reactors + +reactors_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +reactors_SOURCES = \ + test_reactors.cpp + +reactors_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Signals_1.am +noinst_PROGRAMS += signals_1 + +signals_1_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +signals_1_SOURCES = \ + test_signals_1.cpp + +signals_1_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Signals_2.am +noinst_PROGRAMS += signals_2 + +signals_2_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +signals_2_SOURCES = \ + test_signals_2.cpp + +signals_2_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Misc_Timer_Queue.am +noinst_PROGRAMS += timer_queue + +timer_queue_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +timer_queue_SOURCES = \ + test_timer_queue.cpp + +timer_queue_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/Misc/Reactor_Misc.mpc b/ACE/examples/Reactor/Misc/Reactor_Misc.mpc new file mode 100644 index 00000000000..c056a909bd8 --- /dev/null +++ b/ACE/examples/Reactor/Misc/Reactor_Misc.mpc @@ -0,0 +1,57 @@ +// -*- MPC -*- +// $Id$ + +project(*event_handler_t) : aceexe { + exename = event_handler_t + Source_Files { + test_event_handler_t.cpp + } +} +project(*demuxing) : aceexe { + exename = demuxing + Source_Files { + test_demuxing.cpp + } +} +project(*early_timeouts) : aceexe { + exename = early_timeouts + Source_Files { + test_early_timeouts.cpp + } +} +project(*notification) : aceexe { + exename = notification + Source_Files { + notification.cpp + } +} +project(*pingpong) : aceexe { + exename = pingpong + Source_Files { + pingpong.cpp + } +} +project(*reactors) : aceexe { + exename = reactors + Source_Files { + test_reactors.cpp + } +} +project(*signals_1) : aceexe { + exename = signals_1 + Source_Files { + test_signals_1.cpp + } +} +project(*signals_2) : aceexe { + exename = signals_2 + Source_Files { + test_signals_2.cpp + } +} +project(*timer_queue) : aceexe { + exename = timer_queue + Source_Files { + test_timer_queue.cpp + } +} diff --git a/ACE/examples/Reactor/Misc/notification.cpp b/ACE/examples/Reactor/Misc/notification.cpp new file mode 100644 index 00000000000..a04663b28ad --- /dev/null +++ b/ACE/examples/Reactor/Misc/notification.cpp @@ -0,0 +1,385 @@ +// $Id$ + +#include "ace/OS_NS_unistd.h" +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Thread_Manager.h" +#include "ace/Thread.h" +#include "ace/Signal.h" + +ACE_RCSID(Misc, notification, "$Id$") + +#if defined (ACE_HAS_THREADS) +#if defined (CHORUS) +// Chorus does not have signal, so we'll stop after a number of rounds. +#define MAX_ITERATIONS 3 +#else +#define MAX_ITERATIONS 10000 +#endif /* CHORUS */ + +class Thread_Handler : public ACE_Event_Handler +{ + // = TITLE + // Illustrate how the ACE_Reactor's thread-safe event notification + // mechanism works. + // + // = DESCRIPTION + // Handle timeouts in the main thread via the ACE_Reactor and I/O + // events in a separate thread. Just before the separate I/O + // thread exits it notifies the ACE_Reactor in the main thread + // using the ACE_Reactor's notification mechanism. +public: + Thread_Handler (long delay, + long interval, + size_t n_threads, + size_t max_iterations); + // Constructor. + + Thread_Handler (size_t id, + size_t max_iterations); + + ~Thread_Handler (void); + // Destructor. + + virtual int handle_signal (int signum, + siginfo_t * = 0, + ucontext_t * = 0); + // Handle signals. + + virtual int handle_exception (ACE_HANDLE); + // Print data from main thread. + + virtual int handle_output (ACE_HANDLE); + // Print data from main thread. + + virtual int handle_timeout (const ACE_Time_Value &, + const void *); + // Handle timeout events in the main thread. + + virtual int handle_input (ACE_HANDLE); + // General notification messages to the Reactor. + + virtual int notify (ACE_Time_Value *tv = 0); + // Perform notifications. + + virtual int svc (void); + // Handle I/O events in a separate threads. + +private: + static void *svc_run (void *); + // Glues C++ to C thread library functions. + + size_t id_; + // ID passed in by Thread_Handler constructor. + + size_t iterations_; + + static sig_atomic_t shutdown_; + // Shutting down. + + // = Timing variables. + // Delay factor for timer-driven I/O. + static ACE_Time_Value delay_; + + // Interval factor for Event_Handler timer. + static ACE_Time_Value interval_; +}; + +// Shutdown flag. +sig_atomic_t Thread_Handler::shutdown_ = 0; + +// Delay factor for timer-driven I/O. +ACE_Time_Value Thread_Handler::delay_; + +// Interval factor for Event_Handler timer. +ACE_Time_Value Thread_Handler::interval_; + +Thread_Handler::Thread_Handler (size_t id, + size_t max_iterations) + : id_ (id), + iterations_ (max_iterations) +{ +} + +Thread_Handler::~Thread_Handler (void) +{ + // Cleanup resources so that we don't crash and burn when shutdown. + ACE_Event_Handler::remove_stdin_handler (ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()); + ACE_Reactor::instance ()->cancel_timer (this); +} + +Thread_Handler::Thread_Handler ( + long delay, + long interval, + size_t n_threads, + size_t max_iterations) + : iterations_ (max_iterations) +{ + ACE_Sig_Set sig_set; + + sig_set.sig_add (SIGQUIT); + sig_set.sig_add (SIGINT); + + delay_.set (delay); + interval_.set (interval); + this->id_ = 0; + + if (ACE_Event_Handler::register_stdin_handler (this, + ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "register_stdin_handler")); + else if (ACE_Reactor::instance ()->register_handler (sig_set, + this) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "register_handler")); + else if (ACE_Reactor::instance ()->schedule_timer + (this, + 0, + Thread_Handler::delay_, + Thread_Handler::interval_) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "schedule_timer")); + + // Set up this thread's signal mask to block all the signal in the + // <sig_set>, which is inherited by the threads it spawns. + ACE_Sig_Guard guard (&sig_set); + + // Create N new threads of control Thread_Handlers. + + for (size_t i = 0; i < n_threads; i++) + { + Thread_Handler *th; + + ACE_NEW (th, + Thread_Handler (i + 1, + this->iterations_)); + + if (ACE_Thread::spawn (reinterpret_cast<ACE_THR_FUNC> (&Thread_Handler::svc_run), + reinterpret_cast<void *> (th), + THR_NEW_LWP | THR_DETACHED) != 0) + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Thread::spawn")); + } + + // The destructor of <guard> unblocks the signal set so that only + // this thread receives them! +} + +int +Thread_Handler::notify (ACE_Time_Value *timeout) +{ + // Just do something to test the ACE_Reactor's multi-thread + // capabilities... + + if (ACE_Reactor::instance ()->notify + (this, + ACE_Event_Handler::EXCEPT_MASK, + timeout) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "notification::notify:exception"), + -1); + else if (ACE_Reactor::instance ()->notify + (this, + ACE_Event_Handler::WRITE_MASK, + timeout) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "notification::notify:write"), + -1); + return 0; +} + +// Test stdin handling that uses <select> to demultiplex HANDLEs. +// Input is only handled by the main thread. + +int +Thread_Handler::handle_input (ACE_HANDLE handle) +{ + char buf[BUFSIZ]; + ssize_t n = ACE_OS::read (handle, buf, sizeof buf); + + if (n > 0) + { + ACE_DEBUG ((LM_DEBUG, + "input to (%t) %*s", + n, + buf)); + + ACE_DEBUG ((LM_DEBUG, + "%d more input to kill\n", + this->iterations_)); + + // Only wait up to 10 milliseconds to notify the Reactor. + ACE_Time_Value timeout (0, + 10 * 1000); + + if (this->notify (&timeout) == -1) + ACE_ERROR ((LM_DEBUG, + "(%t), %p\n", + "notification::handle_input:notify")); + return 0; + } + else + return -1; +} + +// Perform a task that will test the ACE_Reactor's multi-threading +// capabilities in separate threads. + +int +Thread_Handler::svc (void) +{ + ACE_Time_Value sleep_timeout (0, + // Transform this into microseconds and divide by 2. + (Thread_Handler::interval_.sec () * ACE_ONE_SECOND_IN_USECS) / 2); + + for (int i = this->iterations_; + i > 0; + --i) + { + if (this->shutdown_ != 0) + break; + + // Block for delay_.secs () / 2, then notify the Reactor. + ACE_OS::sleep (sleep_timeout); + + // Wait up to 10 milliseconds to notify the Reactor. + ACE_Time_Value timeout (0, + 10 * 1000); + if (this->notify (&timeout) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "notify")); + } + + ACE_Reactor::instance ()->remove_handler (this, + ALL_EVENTS_MASK); + ACE_DEBUG ((LM_DEBUG, + "(%t) exiting svc\n")); + return 0; +} + +// Test signal handling. + +int +Thread_Handler::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + // @@ Note that this code is not portable to all OS platforms since + // it uses print statements within signal handler context. + ACE_DEBUG ((LM_DEBUG, + "(%t) received signal %S\n", + signum)); + + switch (signum) + { + case SIGINT: + case SIGQUIT: + ACE_ERROR ((LM_ERROR, + "(%t) ******************** shutting down %n on signal %S\n", + signum)); + this->shutdown_ = 1; + ACE_Reactor::end_event_loop(); + } + return 0; +} + +int +Thread_Handler::handle_timeout (const ACE_Time_Value &time, const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) received timeout at (%u, %u), iterations = %d\n", + time.sec (), + time.usec (), + this->iterations_)); + + if (--this->iterations_ <= 0 + || Thread_Handler::interval_.sec () == 0) + ACE_Reactor::end_event_loop (); + + return 0; +} + +// Called by the ACE_Reactor when it receives a notification. + +int +Thread_Handler::handle_exception (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) exception to id %d, iteration = %d\n", + this->id_, + this->iterations_)); + return 0; +} + +// Called by the ACE_Reactor when it receives a notification. + +int +Thread_Handler::handle_output (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) output to id %d, iteration = %d\n", + this->id_, + // This decrement must come last since + // <handle_exception> is called before <handle_output>! + this->iterations_--)); + return 0; +} + +// "Shim" function that integrates C thread API with C++. + +void * +Thread_Handler::svc_run (void *eh) +{ + Thread_Handler *this_handler = + reinterpret_cast<Thread_Handler *> (eh); + + if (this_handler->svc () == 0) + return 0; + else + return reinterpret_cast<void *> (-1); +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_LOG_MSG->open (argv[0]); + + if (argc < 4) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("usage: %s delay interval n_threads [iterations]\n"), + argv[0])); + ACE_OS::exit (1); + } + + int delay = ACE_OS::atoi (argv[1]); + int interval = ACE_OS::atoi (argv[2]); + size_t n_threads = ACE_OS::atoi (argv[3]); + size_t max_iterations = argc > 4 ? ACE_OS::atoi (argv[4]) : MAX_ITERATIONS; + + Thread_Handler thr_handler (delay, + interval, + n_threads, + max_iterations); + + ACE_Reactor::instance ()->run_reactor_event_loop (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("exiting from main\n"))); + return 0; +} +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "threads must be supported to run this application\n"), -1); +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/examples/Reactor/Misc/pingpong.cpp b/ACE/examples/Reactor/Misc/pingpong.cpp new file mode 100644 index 00000000000..f4a3bc0630e --- /dev/null +++ b/ACE/examples/Reactor/Misc/pingpong.cpp @@ -0,0 +1,302 @@ +// $Id$ + +/* Simple program that illustrates many features of the ACE_Reactor: + + 1. I/O event demultiplexing + 2. Signal-based demultiplexing + 3. Timer-based demultiplexing + + To test this program, compile it and then execute it as follows: + + % ./pingpong hello + + You should see lots of the following output: + + writing <4> [7860] + writing <4> [7860] + writing <4> [7860] + writing <4> [7860] + reading <5> (7860) [1] = hello + writing <4> [7860] + writing <5> [7861] + reading <4> (7861) [2] = hello + reading <5> (7860) [2] = hello + writing <4> [7860] + writing <5> [7861] + reading <4> (7861) [3] = hello + reading <5> (7860) [3] = hello + + After 10 seconds you'll see the following: + + ./pingpong: shutting down tester (pid = 7861) + ./pingpong: shutting down tester (pid = 7860) + + and the program will stop. If you'd like to + stop it earlier, just hit the control-C sequence + and you'll see the same messages. */ + +#include "ace/Reactor.h" +#include "ace/Pipe.h" +#include "ace/Log_Msg.h" +#include "ace/ACE.h" +#include "ace/Test_and_Set.h" +#include "ace/OS_NS_string.h" +#include "ace/Null_Mutex.h" +#include "ace/OS_NS_unistd.h" +#if defined (ACE_WIN32) || defined (CHORUS) +# include "ace/Barrier.h" +# include "ace/Thread.h" +#endif + +ACE_RCSID(Misc, pingpong, "$Id$") + +class Ping_Pong : public ACE_Test_and_Set<ACE_Null_Mutex, sig_atomic_t> +{ +public: + Ping_Pong (char b[], ACE_HANDLE f); + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int handle_output (ACE_HANDLE); + virtual int handle_timeout (const ACE_Time_Value &, + const void *); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); +private: + char buf_[BUFSIZ]; + // Buffer to send. + + size_t buflen_; + // Length of the buffer to send. + + int pid_; + // Process ID. + + ACE_HANDLE handle_; + // Open handle. +}; + +Ping_Pong::Ping_Pong (char b[], ACE_HANDLE f) + : buflen_ (ACE_OS::strlen (b) + 1 + (2 * sizeof (int))), + pid_ (ACE_OS::getpid ()), + handle_ (f) +{ + *((int *) this->buf_) = (int) this->pid_; + *((int *) (this->buf_ + sizeof (int))) = 0; + ACE_OS::strcpy (this->buf_ + (2 * sizeof (int)), b); + this->buf_[this->buflen_ - 1] = '\n'; + this->buf_[this->buflen_] = '\0'; +} + +ACE_HANDLE +Ping_Pong::get_handle (void) const +{ + return this->handle_; +} + +int +Ping_Pong::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + delete this; // Cleanup when we're removed from the reactor. + return 0; +} + +int +Ping_Pong::handle_input (ACE_HANDLE) +{ +#if defined (ACE_HAS_STREAM_PIPES) + // We can rely on record-oriented reads... + + ssize_t n = ACE::recv (this->handle_, this->buf_, this->buflen_); + + if (n != (ssize_t) this->buflen_) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("(%P|%t) reading [%d] %p\n"), + handle_, + ACE_TEXT ("read")), + -1); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reading <%d> (%d) [%d] = %C\n"), + this->handle_, + *(int *) this->buf_, + *(int *) (this->buf_ + sizeof (int)), + this->buf_ + (2 * sizeof (int)))); +#else + ssize_t n = ACE::recv (this->handle_, + this->buf_, + this->buflen_); + if (n == -1) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("[%d] %p\n"), + handle_, + ACE_TEXT ("read")), + -1); + n -= (2 * sizeof (int)); + char *buf = this->buf_ + (2 * sizeof (int)); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) reading <%d> = %*C\n"), + this->handle_, + n, + buf)); +#endif /* ACE_HAS_STREAM_PIPES */ + return 0; +} + +int +Ping_Pong::handle_output (ACE_HANDLE) +{ +#if defined (ACE_HAS_STREAM_PIPES) + // We can rely on record-oriented reads... + + (*(int *) (this->buf_)) = this->pid_; + (*(int *) (this->buf_ + sizeof (int)))++; + if (ACE::send (this->handle_, + this->buf_, + this->buflen_) == -1) + return -1; + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) writing <%d> [%d]\n"), + this->handle_, + this->pid_)); + return 0; + } +#else + if (ACE::send (this->handle_, + this->buf_, + this->buflen_) == -1) + return -1; + else + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) writing <%d>\n"), + this->handle_)); + return 0; + } +#endif /* ACE_HAS_STREAM_PIPES */ +} + +int +Ping_Pong::handle_timeout (const ACE_Time_Value &, + const void *) +{ + this->set (1); + return 0; +} + +// Contains the string to "pingpong" back and forth... +static ACE_TCHAR *string_name; + +// Wait for 10 seconds and then shut down. +static const ACE_Time_Value SHUTDOWN_TIME (10); + +static void +run_svc (ACE_HANDLE handle) +{ + Ping_Pong *callback = 0; + ACE_NEW (callback, + Ping_Pong (ACE_TEXT_ALWAYS_CHAR (string_name), + handle)); + + ACE_Reactor reactor; + + // Register the callback object for the various I/O, signal, and + // timer-based events. + + if (reactor.register_handler (callback, + ACE_Event_Handler::READ_MASK + | ACE_Event_Handler::WRITE_MASK) == -1 +#if !defined (CHORUS) + || reactor.register_handler (SIGINT, + callback) == -1 +#endif /* CHORUS */ + || reactor.schedule_timer (callback, + 0, + SHUTDOWN_TIME) == -1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("reactor"))); + ACE_OS::exit (1); + } + + // Main event loop (one per process). + + while (callback->is_set () == 0) + if (reactor.handle_events () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("handle_events"))); +} + +#if defined (ACE_WIN32) || defined (CHORUS) +static ACE_Barrier barrier (3); + +static void * +worker (void *arg) +{ + ACE_HANDLE handle = (ACE_HANDLE) arg; + + run_svc (handle); + + // Wait for the threads to exit. + barrier.wait (); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) %n: shutting down tester\n"))); + return 0; +} +#endif /* ACE_WIN32 */ + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (argc != 2) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("usage: pingpong <string>\n")), + -1); + + ACE_LOG_MSG->open (argv[0]); + + string_name = argv[1]; + + ACE_HANDLE handles[2]; + + // Create a pipe and initialize the handles. + ACE_Pipe pipe (handles); + +#if defined (ACE_WIN32) || defined (CHORUS) + if (ACE_Thread::spawn (ACE_THR_FUNC (worker), + (void *) handles[0], + THR_DETACHED) == -1 + || ACE_Thread::spawn (ACE_THR_FUNC (worker), + (void *) handles[1], + THR_DETACHED) == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n%a"), + ACE_TEXT ("spawn"), + 1)); + barrier.wait (); +#else + pid_t pid = ACE_OS::fork (argv[0]); + + if (pid == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n%a"), + ACE_TEXT ("fork"), + 1)); + run_svc (handles[pid == 0]); + + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%P|%t) %n: shutting down tester\n"))); +#endif /* ACE_WIN32 */ + + if (pipe.close () == -1) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("close"))); + return 0; +} diff --git a/ACE/examples/Reactor/Misc/test_demuxing.cpp b/ACE/examples/Reactor/Misc/test_demuxing.cpp new file mode 100644 index 00000000000..6badd849757 --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_demuxing.cpp @@ -0,0 +1,384 @@ +// $Id$ + +// Perform an extensive test of all the ACE_Reactor's event handler +// dispatching mechanisms. These mechanisms illustrate how I/O, +// timeout, and signal events, as well as ACE_Message_Queues, can all +// be handled within the same demultiplexing and dispatching +// framework. In addition, this example illustrates how to use the +// ACE_Reactor for devices that perform I/O via signals (such as SVR4 +// message queues). + +#include "ace/ACE.h" +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Task.h" +#include "ace/Reactor_Notification_Strategy.h" +#include "ace/Signal.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(Misc, test_demuxing, "$Id$") + +// Default is to have a 2 second timeout. +static int timeout = 2; + +class Sig_Handler : public ACE_Event_Handler +{ + // = TITLE + // This class illustrates how to handle signal-driven I/O using + // the <ACE_Reactor> framework. Note that signals may be caught + // and processed without requiring the use of global signal + // handler functions or global signal handler data. +public: + Sig_Handler (void); + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE); + virtual int shutdown (ACE_HANDLE, ACE_Reactor_Mask); + virtual int handle_signal (int signum, siginfo_t * = 0, + ucontext_t * = 0); + +private: + ACE_HANDLE handle_; +}; + +// A dummy_handle is required to reserve a slot in the ACE_Reactor's +// descriptor table. + +Sig_Handler::Sig_Handler (void) +{ + // Assign the Sig_Handler a dummy I/O descriptor. Note that even + // though we open this file "Write Only" we still need to use the + // ACE_Event_Handler::NULL_MASK when registering this with the + // ACE_Reactor (see below). + this->handle_ = ACE_OS::open (ACE_DEV_NULL, O_WRONLY); + ACE_ASSERT (this->handle_ != ACE_INVALID_HANDLE); + + // Register signal handler object. Note that NULL_MASK is used to + // keep the ACE_Reactor from calling us back on the "/dev/null" + // descriptor. NULL_MASK just reserves a "slot" in the Reactor's + // internal demuxing table, but doesn't cause it to dispatch the + // event handler directly. Instead, we use the signal handler to do + // this. + ACE_Reactor_Mask mask = ACE_Event_Handler::NULL_MASK; + if (ACE_Reactor::instance ()->register_handler + (this, + mask) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); + + // Create a sigset_t corresponding to the signals we want to catch. + ACE_Sig_Set sig_set; + + sig_set.sig_add (SIGINT); + sig_set.sig_add (SIGQUIT); + sig_set.sig_add (SIGALRM); + + // Register the signal handler object to catch the signals. + if (ACE_Reactor::instance ()->register_handler + (sig_set, this) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); +} + +// Called by the ACE_Reactor to extract the handle. + +ACE_HANDLE +Sig_Handler::get_handle (void) const +{ + return this->handle_; +} + +// In a real application, this method would be where the read on the +// signal-driven I/O device would occur asynchronously. For now we'll +// just print a greeting to let you know that everything is working +// properly! + +int +Sig_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) handling asynchonrous input...\n")); + return 0; +} + +// In a real application, this method would do any cleanup activities +// required when shutting down the I/O device. + +int +Sig_Handler::shutdown (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) closing down Sig_Handler...\n")); + return 0; +} + +// This method handles all the signals that are being caught by this +// object. In our simple example, we are simply catching SIGALRM, +// SIGINT, and SIGQUIT. Anything else is logged and ignored. Note +// that the ACE_Reactor's signal handling mechanism eliminates the +// need to use global signal handler functions and data. + +int +Sig_Handler::handle_signal (int signum, siginfo_t *, ucontext_t *) +{ + switch (signum) + { +#if !defined (ACE_WIN32) + case SIGALRM: + // Rearm the alarm. + ACE_OS::alarm (4); + break; +#endif /* !ACE_WIN32 */ + case SIGINT: + // Tell the ACE_Reactor to enable the ready bit for + // this->handle_. The ACE_Reactor will subsequently call the + // <Sig_Handler::handle_input> method from within its event + // loop, i.e., the behavior triggered by the signal is handled + // in the main event loop, rather than in the signal handler. + return ACE_Reactor::instance ()->ready_ops + (this->handle_, + ACE_Event_Handler::READ_MASK, + ACE_Reactor::ADD_MASK); +#if defined (ACE_WIN32) + case SIGTERM: +#else + case SIGQUIT: +#endif /* ACE_WIN32 */ + ACE_Reactor::end_event_loop (); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, "invalid signal"), -1); + break; + /* NOTREACHED */ + } + return 0; +} + +class STDIN_Handler : public ACE_Event_Handler +{ + // = TITLE + // This class illustrates that the ACE_Reactor can handle signals, + // STDIO, and timeouts using the same mechanisms. +public: + STDIN_Handler (void); + ~STDIN_Handler (void); + virtual int handle_input (ACE_HANDLE); + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg); +}; + +STDIN_Handler::STDIN_Handler (void) +{ + if (ACE_Event_Handler::register_stdin_handler (this, + ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "register_stdin_handler")); + + // Register the <STDIN_Handler> to be dispatched once every + // <timeout> seconds starting in <timeout> seconds. This example + // uses the "interval timer" feature of the <ACE_Reactor>'s timer + // queue. + else if (ACE_Reactor::instance ()->schedule_timer + (this, + 0, + ACE_Time_Value (timeout), + ACE_Time_Value (timeout)) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "schedule_timer", + 1)); +} + +STDIN_Handler::~STDIN_Handler (void) +{ + if (ACE_Event_Handler::remove_stdin_handler (ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "remove_stdin_handler")); + else if (ACE_Reactor::instance ()->cancel_timer + (this) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "cancel_timer", + 1)); +} + +int +STDIN_Handler::handle_timeout (const ACE_Time_Value &tv, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) timeout occurred at %d sec, %d usec\n", + tv.sec (), + tv.usec ())); + return 0; +} + +// Read from input handle and write to stdout handle. + +int +STDIN_Handler::handle_input (ACE_HANDLE handle) +{ + char buf[BUFSIZ]; + ssize_t n = ACE_OS::read (handle, buf, sizeof buf); + + switch (n) + { + case -1: + if (errno == EINTR) + return 0; + /* NOTREACHED */ + else + ACE_ERROR ((LM_ERROR, + "%p\n", + "read")); + /* FALLTHROUGH */ + case 0: + ACE_Reactor::end_event_loop (); + break; + default: + { + ssize_t result = ACE::write_n (ACE_STDOUT, buf, n); + + if (result != n) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "write"), + result == -1 && errno == EINTR ? 0 : -1); + } + } + return 0; +} + +class Message_Handler : public ACE_Task <ACE_SYNCH> +{ +public: + Message_Handler (void); + + virtual int handle_input (ACE_HANDLE); + // Called back within the context of the <ACE_Reactor> Singleton to + // dequeue and process the message on the <ACE_Message_Queue>. + + virtual int svc (void); + // Run the "event-loop" periodically putting messages to our + // internal <Message_Queue> that we inherit from <ACE_Task>. + +private: + ACE_Reactor_Notification_Strategy notification_strategy_; + // This strategy will notify the <ACE_Reactor> Singleton when a new + // message is enqueued. +}; + +Message_Handler::Message_Handler (void) + : notification_strategy_ (ACE_Reactor::instance (), + this, + ACE_Event_Handler::READ_MASK) +{ + // Set this to the Reactor notification strategy. + this->msg_queue ()->notification_strategy (&this->notification_strategy_); + + if (this->activate ()) + ACE_ERROR ((LM_ERROR, + "%p\n", + "activate")); +} + +int +Message_Handler::svc (void) +{ + for (int i = 0;; i++) + { + ACE_Message_Block *mb; + + ACE_NEW_RETURN (mb, + ACE_Message_Block (1), + 0); + + mb->msg_priority (i); + ACE_OS::sleep (1); + + // Note that this putq() call with automagically invoke the + // notify() hook of our ACE_Reactor_Notification_Strategy, + // thereby informing the <ACE_Reactor> Singleton to call our + // <handle_input> method. + if (this->putq (mb) == -1) + { + if (errno == ESHUTDOWN) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) queue is deactivated"), 0); + else + ACE_ERROR_RETURN ((LM_ERROR, + "(%t) %p\n", + "putq"), + -1); + } + } + + ACE_NOTREACHED (return 0); +} + +int +Message_Handler::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) Message_Handler::handle_input\n")); + + ACE_Message_Block *mb; + + if (this->getq (mb, (ACE_Time_Value *) &ACE_Time_Value::zero) == -1) + ACE_ERROR ((LM_ERROR, + "(%t) %p\n", + "dequeue_head")); + else + { + ACE_DEBUG ((LM_DEBUG, + "(%t) priority = %d\n", + mb->msg_priority ())); + mb->release (); + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_Service_Config daemon (argv [0]); + + // Optionally start the alarm. + if (argc > 1) + { + ACE_OS::alarm (4); + timeout = ACE_OS::atoi (argv[1]); + } + + // Signal handler. + Sig_Handler sh; + + // Define an I/O handler object. + STDIN_Handler ioh; + + // Define a message handler. + Message_Handler mh; + + // Loop handling signals and I/O events until SIGQUIT occurs. + + while (ACE_Reactor::instance ()->event_loop_done () == 0) + ACE_Reactor::instance ()->run_reactor_event_loop (); + + // Deactivate the message queue. + mh.msg_queue ()->deactivate (); + + // Wait for the thread to exit. + ACE_Thread_Manager::instance ()->wait (); + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("(%t) leaving main\n"))); + return 0; +} diff --git a/ACE/examples/Reactor/Misc/test_early_timeouts.cpp b/ACE/examples/Reactor/Misc/test_early_timeouts.cpp new file mode 100644 index 00000000000..4c7193d9a67 --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_early_timeouts.cpp @@ -0,0 +1,114 @@ +// $Id$ + +// ================================================================ +// +// = LIBRARY +// examples/Reactor/Misc/ +// +// = FILENAME +// test_early_timeouts.cpp +// +// = DESCRIPTION +// On some platforms, select() returns before the time value +// specified. This tests counts the number of times this happens +// and the max early timeout. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ================================================================ + +#include "ace/Handle_Set.h" +#include "ace/Pipe.h" +#include "ace/Log_Msg.h" +#include "ace/Time_Value.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_sys_select.h" + +ACE_RCSID(Misc, test_early_timeouts, "$Id$") + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + // Mumber of seconds this test should run + int runtime_in_seconds = 10; + + // Iterations + int iterations = runtime_in_seconds * 10; + + // 100 millisecond timeout + ACE_Time_Value timeout (0, 100000); + + // Time before starting select + ACE_Time_Value starting_time_of_day; + + // Time before starting select + ACE_Time_Value ending_time_of_day; + + // Number of times the timer expired early + int no_of_early_timers = 0; + + // Maximum early timeout + ACE_Time_Value maximum_early_timeout; + + // + // Dummy handle and handle set + // Note that some OS do not like "empty selects" + // + + // Dummy handle set + ACE_Handle_Set dummy_handle_set; + + // Dummy pipe + ACE_Pipe dummy_pipe; + int result = dummy_pipe.open (); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); // To avoid compile warning with ACE_NDEBUG. + + for (int i = 1; i <= iterations; i++) + { + // Add dummy handle to dummy set + dummy_handle_set.set_bit (dummy_pipe.read_handle ()); + + // Note the time before select + starting_time_of_day = ACE_OS::gettimeofday (); + + // Wait for timeout + result = ACE_OS::select ((int) dummy_pipe.read_handle (), dummy_handle_set, 0, 0, &timeout); + ACE_ASSERT (result == 0); + + // Note the time after select + ending_time_of_day = ACE_OS::gettimeofday (); + + // Expected ending time + ACE_Time_Value expected_ending_time_of_day = + starting_time_of_day + timeout; + + // If the timer expired early + if (ending_time_of_day < expected_ending_time_of_day) + { + // How early + ACE_Time_Value early_timeout = expected_ending_time_of_day - ending_time_of_day; + + // Increment number of early timers + no_of_early_timers++; + + // Check max early timeout + if (early_timeout > maximum_early_timeout) + { + maximum_early_timeout = early_timeout; + } + } + } + + ACE_DEBUG ((LM_DEBUG, + "There were %d early timers out of %d calls to select() (%f%%)\n" + "The max early timeout was: %dsec %dusec\n", + no_of_early_timers, + iterations, + float (no_of_early_timers) / iterations * 100, + maximum_early_timeout.sec (), + maximum_early_timeout.usec ())); + + return 0; +} diff --git a/ACE/examples/Reactor/Misc/test_event_handler_t.cpp b/ACE/examples/Reactor/Misc/test_event_handler_t.cpp new file mode 100644 index 00000000000..4261859784e --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_event_handler_t.cpp @@ -0,0 +1,47 @@ +// $Id$ + +#include "ace/Event_Handler_T.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(Misc, test_event_handler_t, "$Id$") + +#if defined (ACE_HAS_TEMPLATE_TYPEDEFS) + +class ACE_Test_Sig_Handler +{ +public: + ACE_Test_Sig_Handler (void) {} + virtual ~ACE_Test_Sig_Handler (void) {} + virtual ACE_HANDLE get_handle (void) const { return 0; } + virtual void set_handle (ACE_HANDLE) {} + virtual int handle_async_io (ACE_HANDLE) { return 0; } + virtual int shutdown (ACE_HANDLE, ACE_Reactor_Mask) { return 0; } + virtual int signal_handler (int /* signum */, + siginfo_t * = 0, + ucontext_t * = 0) + { + return 0; + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + typedef ACE_Event_Handler_T<ACE_Test_Sig_Handler> EH_SH; + + // Tie the ACE_Event_Handler_T together with the methods from ACE_Test_Sig_Handler. + EH_SH tied_sh (new ACE_Test_Sig_Handler, 1, + &ACE_Test_Sig_Handler::get_handle, + &ACE_Test_Sig_Handler::handle_async_io, + &ACE_Test_Sig_Handler::shutdown, + &ACE_Test_Sig_Handler::signal_handler); + return 0; +} + +#else +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, "your platform does not support template typedefs\n"), 1); +} +#endif /* ACE_HAS_TEMPLATE_TYPEDEFS */ diff --git a/ACE/examples/Reactor/Misc/test_reactors.cpp b/ACE/examples/Reactor/Misc/test_reactors.cpp new file mode 100644 index 00000000000..a08580672e0 --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_reactors.cpp @@ -0,0 +1,195 @@ +// $Id$ + +// Perform a torture test of multiple ACE_Reactors and ACE_Tasks in +// the same process... Thanks to Detlef Becker for contributing this. + +#include "ace/Reactor.h" +#include "ace/Service_Config.h" +#include "ace/Task.h" +#include "ace/Atomic_Op.h" + +ACE_RCSID(Misc, test_reactors, "$Id$") + +#if defined (ACE_HAS_THREADS) + +#include "ace/Recursive_Thread_Mutex.h" + +static const int NUM_INVOCATIONS = 10; +static const int MAX_TASKS = 20; + +class Test_Task : public ACE_Task<ACE_MT_SYNCH> +{ +public: + Test_Task (void); + ~Test_Task (void); + + virtual int open (void *args = 0); + virtual int close (u_long flags = 0); + virtual int svc (void); + + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE fd, + ACE_Reactor_Mask close_mask); + +private: + int handled_; + + static int task_count_; +}; + +int Test_Task::task_count_ = 0; + +static ACE_Atomic_Op<ACE_Thread_Mutex, int> done_count = MAX_TASKS * 2; + +static ACE_Recursive_Thread_Mutex reclock_; + +Test_Task::Test_Task (void) + : handled_ (0) +{ + ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, reclock_); + + Test_Task::task_count_++; + ACE_DEBUG ((LM_DEBUG, + "(%t) TT+ Test_Task::task_count_ = %d\n", + Test_Task::task_count_)); +} + +Test_Task::~Test_Task (void) +{ + ACE_GUARD (ACE_Recursive_Thread_Mutex, ace_mon, reclock_); + + ACE_DEBUG ((LM_DEBUG, + "(%t) TT- Test_Task::task_count_ = %d\n", + Test_Task::task_count_)); +} + +int +Test_Task::open (void *args) +{ + this->reactor ((ACE_Reactor *) args); + return this->activate (THR_NEW_LWP); +} + +int +Test_Task::close (u_long) +{ + ACE_GUARD_RETURN (ACE_Recursive_Thread_Mutex, ace_mon, reclock_, -1); + + Test_Task::task_count_--; + ACE_DEBUG ((LM_DEBUG, + "(%t) close Test_Task::task_count_ = %d\n", + Test_Task::task_count_)); + return 0; +} + +int +Test_Task::svc (void) +{ + for (int i = 0; i < NUM_INVOCATIONS; i++) + { + ACE_OS::thr_yield (); + + // ACE_DEBUG ((LM_DEBUG, "(%t) calling notify %d\n", i)); + + if (this->reactor ()->notify (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "notify"), -1); + + // ACE_DEBUG ((LM_DEBUG, "(%t) leaving notify %d\n", i)); + } + + return 0; +} + +int +Test_Task::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) handle_close\n")); + return 0; +} + +int +Test_Task::handle_input (ACE_HANDLE) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) handle_input\n")); + + this->handled_++; + + if (this->handled_ == NUM_INVOCATIONS) + { + done_count--; + ACE_DEBUG ((LM_DEBUG, + "(%t) handle_input, handled_ = %d, done_count = %d\n", + this->handled_, done_count.value ())); + } + + ACE_OS::thr_yield (); + return -1; +} + +static void * +worker (void *args) +{ + ACE_Reactor *reactor = (ACE_Reactor *) args; + + reactor->owner (ACE_Thread::self ()); + + ACE_Time_Value timeout (4); + + for (;;) + { + //ACE_DEBUG ((LM_DEBUG, "(%t) calling handle_events\n")); + + switch (reactor->handle_events (timeout)) + { + case -1: + ACE_ERROR_RETURN ((LM_ERROR, "(%t) %p\n", "reactor"), 0); + /* NOTREACHED */ + case 0: + ACE_ERROR_RETURN ((LM_ERROR, "(%t) timeout\n"), 0); + /* NOTREACHED */ + } + + // ACE_DEBUG ((LM_DEBUG, "(%t) done with handle_events\n")); + + } + + ACE_NOTREACHED(return 0); +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_Reactor *react1 = ACE_Reactor::instance (); + ACE_Reactor *react2 = new ACE_Reactor (); + Test_Task tt1[MAX_TASKS]; + Test_Task tt2[MAX_TASKS]; + + for (int i = 0; i < MAX_TASKS; i++) + { + tt1[i].open (react1); + tt2[i].open (react2); + } + + if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (worker), (void *) react1, THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "spawn"), -1); + + else if (ACE_Thread_Manager::instance ()->spawn + (ACE_THR_FUNC (worker), (void *) react2, THR_NEW_LWP) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "spawn"), -1); + + ACE_Thread_Manager::instance ()->wait (); + ACE_DEBUG ((LM_DEBUG, "(%t) done\n")); + + return 0; +} + +#else +int +main (int, char *[]) +{ + ACE_ERROR ((LM_ERROR, "threads not supported on this platform\n")); + return 0; +} +#endif /* ACE_HAS_THREADS */ diff --git a/ACE/examples/Reactor/Misc/test_signals_1.cpp b/ACE/examples/Reactor/Misc/test_signals_1.cpp new file mode 100644 index 00000000000..b0410f572c1 --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_signals_1.cpp @@ -0,0 +1,114 @@ +// $Id$ + +// This simple program illustrates the difference between handling +// signals via the Reactor (which doesn't cause the event loop to +// terminate) and signals that aren't handled via the Reactor (which +// do). + +#include "ace/Service_Config.h" +#include "ace/Reactor.h" +#include "ace/Log_Msg.h" +#include "ace/Signal.h" + +ACE_RCSID(Misc, test_signals_1, "$Id$") + +// Number of times to allow signal to execute until we quit. +static size_t count = 10; + +static void +my_signal_function (int sig) +{ + ACE_DEBUG ((LM_DEBUG, + "Executed non-ACE signal handler for signal %S\n", + sig)); +} + +class My_Handler : public ACE_Event_Handler +{ +public: + virtual int handle_signal (int sig, + siginfo_t *, + ucontext_t *) + { + // @@ Note that this code is not portable to all OS platforms + // since it uses print statements within signal handler context. + ACE_DEBUG ((LM_DEBUG, + "Executed ACE signal handler for signal %S, count = %d\n", + sig, + count)); + count--; + + if (count == 0) + ACE_Reactor::end_event_loop (); + + return 0; + } + + virtual int handle_timeout (const ACE_Time_Value &, + const void *arg) + { + ACE_DEBUG ((LM_DEBUG, + "%s\n", + (const char *) arg)); + return 0; + } +}; + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + // First you need a handler for the timeout. + My_Handler my_handler; + + // This is the timeout period in seconds. + ACE_Time_Value period (ACE_DEFAULT_TIMEOUT); + + if (argc > 1) + period.set (ACE_OS::atoi (argv[1]), 0); + + // Set up the periodic interval timer. + if (ACE_Reactor::instance ()->schedule_timer + (&my_handler, + "hello", + period, + period) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "schedule_timer"), + -1); + + // Set up an ACE signal handler. + + if (ACE_Reactor::instance ()->register_handler + (SIGINT, + &my_handler) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "register_handler"), + -1); + + // Set up a non-ACE signal handler. When this goes off, the Reactor + // should return from its <run_event_loop> method. + ACE_Sig_Action sig ((ACE_SignalHandler) my_signal_function, + SIGQUIT); + ACE_UNUSED_ARG (sig); + + ACE_DEBUG ((LM_DEBUG, + "starting event loop that runs until you've typed ^C a total of 10 times or ^\\ once.\n")); + + // This call executes the reactor events until we're finished. + int result = ACE_Reactor::run_event_loop (); + + ACE_DEBUG ((LM_DEBUG, + "result = %d\n", + result)); + + // Make sure to remove my_handler before exiting main() since + // otherwise weird things can happen... + if (ACE_Reactor::instance ()->cancel_timer (&my_handler) == -1) + ACE_ERROR_RETURN ((LM_DEBUG, + "%p\n", + "cancel_timer"), + -1); + return 0; +} diff --git a/ACE/examples/Reactor/Misc/test_signals_2.cpp b/ACE/examples/Reactor/Misc/test_signals_2.cpp new file mode 100644 index 00000000000..466ab58482f --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_signals_2.cpp @@ -0,0 +1,291 @@ +// $Id$ + +// Test the ability of the Reactor/Signal_Handler to register multiple +// handler per-signal. + +/* This test works as follows: + + 1. To test the "original" semantics of ACE (i.e., only one + ACE_Event_Handler can be registered per signal), you don't + need to do anything special. Existing programs work the + same since giving the Reactor's constructor a 0 value + (which is the default argument, BTW) instructs it to behave + as before. When a 0 is given, the ACE_Reactor's + constructor/open method creates an instance of + ACE_Sig_Handler and assigns this to an internal pointer. + This pointer is then used to dispatch all signal-related + methods within the Reactor. The default ACE_Sig_Handler + only allows *one* ACE_Event_Handler to be registered + per-signal. + + To run this version of the test do the following: + + % ./test-signal + ./test_signals + waiting for SIGINT or SIGQUIT + ^C + signal Interrupt occurred in Sig_Handler_2 (fruity, 0, 0) with count = 1 + waiting for SIGINT or SIGQUIT + ^\ + signal Quit occurred in Sig_Handler_2 (fruity, 0, 0) with count = 2 + shutting down SIGQUIT in Sig_Handler_2 (fruity, 0, 0) + waiting for SIGINT or SIGQUIT + ^C + signal Interrupt occurred in Sig_Handler_2 (fruity, 0, 0) with count = 3 + waiting for SIGINT or SIGQUIT + ^\Quit (core dumped) + + Note that in this test only one handler (the last one -- + "Sig_Handler_2 (fruity)") is actually registered. BTW, the + core dump is the expected behavior since the default + disposition is restored when there are no more handlers + (see the code below). + + 2. To test the "multiple handlers per-signal semantics", you + need to pass the constructor/open method of the ACE_Reactor + a pointer to a an instance of ACE_Sig_Handlers (note the + plural "s"). ACE_Sig_Handlers is a class that derives from + ACE_Sig_Handler. The difference between these two classes + is that (1) ACE_Sig_Handlers::register_signal allows + multiple ACE_Event_Handlers to be registered per-signal and + (2) it enables SA_RESTART by default. This class also + implements Detlef Becker's algorithm for integrating ACE + signal handling with 3rd party libraries. + + To run this version of the test do the following: + + % ./test_signals 1 + + waiting for SIGINT or SIGQUIT + ^C + signal Interrupt occurred in external handler! + signal Interrupt occurred in Sig_Handler_1 (howdy, 3, 1) with count = 1 + shutting down SIGINT in Sig_Handler_1 (howdy, 3, 1) + signal Interrupt occurred in Sig_Handler_1 (doody, 5, 4) with count = 1 + shutting down SIGINT in Sig_Handler_1 (doody, 5, 4) + signal Interrupt occurred in Sig_Handler_2 (tutty, 7, 6) with count = 1 + signal Interrupt occurred in Sig_Handler_2 (fruity, 9, 8) with count = 1 + waiting for SIGINT or SIGQUIT + ^\ + signal Quit occurred in Sig_Handler_1 (howdy, 3, 1) with count = 2 + shutting down SIGQUIT in Sig_Handler_1 (howdy, 3, 1) + signal Quit occurred in Sig_Handler_1 (doody, 5, 4) with count = 2 + shutting down SIGQUIT in Sig_Handler_1 (doody, 5, 4) + signal Quit occurred in Sig_Handler_2 (tutty, 7, 6) with count = 2 + shutting down SIGQUIT in Sig_Handler_2 (tutty, 7, 6) + signal Quit occurred in Sig_Handler_2 (fruity, 9, 8) with count = 2 + shutting down SIGQUIT in Sig_Handler_2 (fruity, 9, 8) + waiting for SIGINT or SIGQUIT + ^C + signal Interrupt occurred in external handler! + signal Interrupt occurred in Sig_Handler_2 (tutty, 7, 6) with count = 3 + signal Interrupt occurred in Sig_Handler_2 (fruity, 9, 8) with count = 3 + waiting for SIGINT or SIGQUIT + ^\Quit (core dumped) + + When this test begins all four handlers are registered and + dispatched when a SIGINT or SIGQUIT occurs. After the + first SIGINT, the handle_signal method of the Sig_Handler_1 + objects unregister themselves. At that point there are 4 + SIGQUIT handlers left, but only 2 of our SIGINT handlers + left (and the 1 external handler). After the first + SIGQUIT, there are no SIGQUIT handlers left since they all + deregister themselves (which restores the "SIG_DFL" + disposition). On the second SIGINT there are only 3 + handlers left (2 of ours and 1 external). Finally, on the + second SIGQUIT we exit and dump core since that's what + happens with the default disposition for SIGQUIT. */ + + +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/Log_Msg.h" +#include "ace/Signal.h" + +ACE_RCSID(Misc, test_signals_2, "$Id$") + +class Sig_Handler_1 : public ACE_Event_Handler +{ +public: + Sig_Handler_1 (ACE_Reactor &reactor, const char *msg) + : msg_ (msg), + count_ (0), + reactor_ (reactor) + { + // Register the signal handlers. + this->quit_sigkey_ = + reactor.register_handler (SIGQUIT, this); + this->int_sigkey_ = + reactor.register_handler (SIGINT, this); + + if (this->quit_sigkey_ == -1 || this->int_sigkey_ == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "register_handler")); + } + + // @@ Note that this code is not portable to all OS platforms since + // it does print statements within the signal handler. + virtual int handle_signal (int signum, + siginfo_t *, + ucontext_t *) + { + this->count_++; + ACE_DEBUG ((LM_DEBUG, + "\nsignal %S occurred in Sig_Handler_1 (%s, %d, %d) with count = %d", + signum, + this->msg_, + this->int_sigkey_, + this->quit_sigkey_, + this->count_)); + + if (this->count_ != 1 && signum == SIGQUIT) + { + if (this->reactor_.remove_handler (SIGQUIT, + 0, + 0, + this->quit_sigkey_) == -1) + ACE_ERROR ((LM_ERROR, + "\n%p", + "remove_handler")); + else + ACE_DEBUG ((LM_DEBUG, + "\nshutting down SIGQUIT in Sig_Handler_1 (%s, %d, %d)", + this->msg_, + this->int_sigkey_, + this->quit_sigkey_)); + } + else if (this->count_ != 2 && signum == SIGINT) + { + if (this->reactor_.remove_handler (SIGINT, + 0, + 0, + this->int_sigkey_) == -1) + ACE_ERROR ((LM_ERROR, + "\n%p", + "remove_handler")); + else + ACE_DEBUG ((LM_DEBUG, + "\nshutting down SIGINT in Sig_Handler_1 (%s, %d, %d)", + this->msg_, + this->int_sigkey_, + this->quit_sigkey_)); + } + return 0; + } + +protected: + const char *msg_; + int count_; + int int_sigkey_; + int quit_sigkey_; + ACE_Reactor &reactor_; +}; + +class Sig_Handler_2 : public Sig_Handler_1 +{ +public: + Sig_Handler_2 (ACE_Reactor &reactor, const char *msg) + : Sig_Handler_1 (reactor, msg) + { + } + + virtual int handle_signal (int signum, + siginfo_t *, + ucontext_t *) + { + this->count_++; + ACE_DEBUG ((LM_DEBUG, + "\nsignal %S occurred in Sig_Handler_2 (%s, %d, %d) with count = %d", + signum, + this->msg_, + this->int_sigkey_, + this->quit_sigkey_, + this->count_)); + if (this->count_ != 0 && signum == SIGQUIT) + { + if (this->reactor_.remove_handler (SIGQUIT, 0, 0, + this->quit_sigkey_) == -1) + ACE_ERROR ((LM_ERROR, + "\n%p", + "remove_handler")); + else + ACE_DEBUG ((LM_DEBUG, + "\nshutting down SIGQUIT in Sig_Handler_2 (%s, %d, %d)", + this->msg_, + this->int_sigkey_, + this->quit_sigkey_)); + } + return 0; + } +}; + +static void +external_handler (int signum) +{ + ACE_DEBUG ((LM_DEBUG, + "\nsignal %S occurred in external handler!", + signum)); +} + +#if !defined (HPUX) +int +ACE_TMAIN (int argc, ACE_TCHAR *[]) +{ + // If argc > 1 then allow multiple handlers per-signal, else just + // allow 1 handler per-signal. + ACE_Sig_Handlers multi_handlers; + +#if defined (ACE_WIN32) + ACE_WFMO_Reactor reactor_impl (argc > 1 + ? &multi_handlers + : (ACE_Sig_Handler *) 0); +#else + ACE_Select_Reactor reactor_impl (argc > 1 + ? &multi_handlers + : (ACE_Sig_Handler *) 0); +#endif /* ACE_WIN32 */ + ACE_Reactor reactor (&reactor_impl); + + if (argc > 1) + { + // Register an "external" signal handler so that the + // ACE_Sig_Handlers code will have something to incorporate! + + ACE_SignalHandler eh = (ACE_SignalHandler) external_handler; + ACE_Sig_Action sa (eh); + + sa.register_action (SIGINT); + } + + // Create a bevy of handlers. + Sig_Handler_1 h1 (reactor, "howdy"); + Sig_Handler_1 h2 (reactor, "doody"); + Sig_Handler_2 h3 (reactor, "tutty"); + Sig_Handler_2 h4 (reactor, "fruity"); + + // Wait for user to type SIGINT and SIGQUIT. + + for (;;) + { + ACE_DEBUG ((LM_DEBUG, + "\nwaiting for SIGINT or SIGQUIT\n")); + + if (reactor.handle_events () == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "handle_events")); + } + + ACE_NOTREACHED (return 0); +} +#else +int +main (int, char *[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "The HP C++ compiler is too lame to support this feature\n"), + -1); +} +#endif /* HPUX */ diff --git a/ACE/examples/Reactor/Misc/test_time_value.cpp b/ACE/examples/Reactor/Misc/test_time_value.cpp new file mode 100644 index 00000000000..a1c1c874b78 --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_time_value.cpp @@ -0,0 +1,83 @@ +// $Id$ + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/Log_Msg.h" +#include "ace/Time_Value.h" + +ACE_RCSID(Misc, test_time_value, "$Id$") + +inline int my_abs (int d) { return d > 0 ? d : -d; } + +ostream & +operator<< (ostream &stream, const ACE_Time_Value &tv) +{ + if (tv.usec () < 0 || tv.sec () < 0) + stream << "-"; + + stream << my_abs (int (tv.sec ())) << "." +// << setw (6) << setfill ('0') + << my_abs (int (tv.usec ())); + return stream; +} + +int +main (int, char *[]) +{ + ACE_Time_Value tv1; + ACE_Time_Value tv2 (2); + ACE_Time_Value tv3 (100); + ACE_Time_Value tv4 (1, 1000000); + ACE_Time_Value tv5 (2); + ACE_Time_Value tv6 (1, -1000000); + + ACE_ASSERT (tv1 == ACE_Time_Value (0)); + ACE_ASSERT (tv2 < tv3); + ACE_ASSERT (tv2 <= tv2); + ACE_ASSERT (tv2 >= tv4); + ACE_ASSERT (tv5 >= tv6); + ACE_ASSERT (tv2 == ACE_Time_Value (1, 1000000)); + ACE_ASSERT (tv5 == tv4); + ACE_ASSERT (tv2 == tv4); + ACE_ASSERT (tv1 != tv2); + ACE_ASSERT (tv6 == tv1); + +# if defined (ACE_NDEBUG) + ACE_UNUSED_ARG (tv1); + ACE_UNUSED_ARG (tv2); + ACE_UNUSED_ARG (tv3); + ACE_UNUSED_ARG (tv4); + ACE_UNUSED_ARG (tv5); + ACE_UNUSED_ARG (tv6); +# endif /* ACE_NDEBUG */ + + cout << "0,0 :\t\t" << ACE_Time_Value (0,0) << endl; + cout << "-0,0 :\t\t" << ACE_Time_Value (-0,0) << endl; + cout << "0,-0 :\t\t" << ACE_Time_Value (0,-0) << endl; + cout << "-0,-0 :\t\t" << ACE_Time_Value (-0,-0) << endl; + cout << endl; + + cout << "0,1 :\t\t" << ACE_Time_Value (0,1) << endl; + cout << "1,0 :\t\t" << ACE_Time_Value (1,0) << endl; + cout << "-1,0 :\t\t" << ACE_Time_Value (-1,0) << endl; + cout << "-1,-0 :\t\t" << ACE_Time_Value (-1,-0) << endl; + cout << endl; + + cout << "1,1 :\t\t" << ACE_Time_Value (1,1) << endl; + cout << "-1,1 :\t\t" << ACE_Time_Value (-1,1) << endl; + cout << "1,-1 :\t\t" << ACE_Time_Value (1,-1) << endl; + cout << "-1,-1 :\t\t" << ACE_Time_Value (-1,-1) << endl; + cout << endl; + + cout << "1,-1111111 :\t" << ACE_Time_Value (1,-1111111) << endl; + cout << "1,-100000 :\t" << ACE_Time_Value (1,-100000) << endl; + cout << "1,-1000000 :\t" << ACE_Time_Value (1,-1000000) << endl; + cout << "-1,1000000 :\t" << ACE_Time_Value (-1,1000000) << endl; + cout << "5,-1000000 :\t" << ACE_Time_Value (5,-1000000) << endl; + cout << "5,-1500000 :\t" << ACE_Time_Value (5,-1500000) << endl; + cout << "2,-2500000 :\t" << ACE_Time_Value (2,-2500000) << endl; + cout << "2,-4500000 :\t" << ACE_Time_Value (2,-4500000) << endl; + + return 0; +} diff --git a/ACE/examples/Reactor/Misc/test_timer_queue.cpp b/ACE/examples/Reactor/Misc/test_timer_queue.cpp new file mode 100644 index 00000000000..64138e13daf --- /dev/null +++ b/ACE/examples/Reactor/Misc/test_timer_queue.cpp @@ -0,0 +1,115 @@ +// $Id$ + +#include "ace/OS_NS_sys_time.h" +#include "ace/Timer_Heap.h" +#include "ace/Timer_List.h" +#include "ace/Timer_Queue.h" +#include "ace/Log_Msg.h" +#include "ace/Recursive_Thread_Mutex.h" +#include "ace/Null_Mutex.h" + +ACE_RCSID(Misc, test_timer_queue, "$Id$") + +class Example_Handler : public ACE_Event_Handler +{ +public: + Example_Handler (void) + : count_ (1) + {} + + virtual int handle_timeout (const ACE_Time_Value &, const void *arg) + { + int *times = (int *) arg; + + ACE_DEBUG ((LM_DEBUG, + "yow, the time has come and gone %d times %d, Horatio!\n", + this->count_++, + *times)); + delete times; + return 0; + } + +private: + int count_; +}; + +static void +test_functionality (ACE_Timer_Queue *tq) +{ + Example_Handler eh; + + ACE_ASSERT (tq->is_empty ()); + ACE_ASSERT (ACE_Time_Value::zero == ACE_Time_Value (0)); + const void *timer_act = 0; + + ACE_NEW (timer_act, int (1)); + long timer_id1 = tq->schedule (&eh, timer_act, ACE_OS::gettimeofday ()); + + // Use timer_id outside of an assert, so that we don't get compile + // warnings with ACE_NDEBUG about it being unused. + if (timer_id1 == -1) + ACE_ERROR ((LM_ERROR, "%p\n", "schedule () failed")); + ACE_ASSERT (timer_id1 != -1); + + ACE_NEW (timer_act, int (42)); + long result = tq->schedule (&eh, timer_act, ACE_OS::gettimeofday ()); + ACE_ASSERT (result != -1); + ACE_NEW (timer_act, int (42)); + result = tq->schedule (&eh, timer_act, ACE_OS::gettimeofday ()); + ACE_ASSERT (result != -1); + + result = tq->cancel (timer_id1, &timer_act); + ACE_ASSERT (result == 1); + delete (int *) timer_act; + result = tq->is_empty (); + ACE_ASSERT (!result); + + result = tq->expire (); + ACE_ASSERT (result == 2); + + ACE_NEW (timer_act, int (4)); + timer_id1 = tq->schedule (&eh, timer_act, ACE_OS::gettimeofday ()); + ACE_ASSERT (timer_id1 != -1); + ACE_NEW (timer_act, int (5)); + long timer_id2 = tq->schedule (&eh, timer_act, ACE_OS::gettimeofday ()); + ACE_ASSERT (timer_id2 != -1); + + result = tq->cancel (timer_id1, &timer_act); + ACE_ASSERT (result == 1); + delete (int *) timer_act; + result = tq->cancel (timer_id2, &timer_act); + ACE_ASSERT (result == 1); + delete (int *) timer_act; + result = tq->is_empty (); + ACE_ASSERT (result == 1); + result = tq->expire (); + ACE_ASSERT (result == 0); +} + +struct Timer_Queues +{ + ACE_Timer_Queue *queue_; + // Pointer to the subclass of <ACE_Timer_Queue> that we're testing. + + const char *name_; + // Name of the Queue that we're testing. +}; + +static Timer_Queues timer_queues[] = +{ + { new ACE_Timer_List, "ACE_Timer_List" }, + { new ACE_Timer_Heap, "ACE_Timer_Heap" }, + { 0, 0 }, +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + for (int i = 0; timer_queues[i].name_ != 0; i++) + { + test_functionality (timer_queues[i].queue_); + delete timer_queues[i].queue_; + } + + return 0; +} diff --git a/ACE/examples/Reactor/Multicast/.cvsignore b/ACE/examples/Reactor/Multicast/.cvsignore new file mode 100644 index 00000000000..955ffdc75d5 --- /dev/null +++ b/ACE/examples/Reactor/Multicast/.cvsignore @@ -0,0 +1,4 @@ +client +client +server +server diff --git a/ACE/examples/Reactor/Multicast/Log_Wrapper.cpp b/ACE/examples/Reactor/Multicast/Log_Wrapper.cpp new file mode 100644 index 00000000000..055b57b9975 --- /dev/null +++ b/ACE/examples/Reactor/Multicast/Log_Wrapper.cpp @@ -0,0 +1,81 @@ +// $Id$ + +// client.C + +#include "Log_Wrapper.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_sys_utsname.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_netdb.h" + +ACE_RCSID(Multicast, Log_Wrapper, "$Id$") + +Log_Wrapper::Log_Wrapper (void) +{ + sequence_number_ = 0; + this->log_msg_.app_id = ACE_OS::getpid (); +} + +Log_Wrapper::~Log_Wrapper (void) +{ +} + +// Set the log_msg_ host address. + +int +Log_Wrapper::open (const int port, const char *mcast_addr) +{ + struct hostent *host_info; + ACE_utsname host_data; + + if (ACE_OS::uname (&host_data) < 0) + return -1; + +#if defined (ACE_LACKS_UTSNAME_T) + if ((host_info = ACE_OS::gethostbyname + (ACE_TEXT_ALWAYS_CHAR(host_data.nodename))) == NULL) +#else + if ((host_info = ACE_OS::gethostbyname (host_data.nodename)) == NULL) +#endif + return -1; + else + ACE_OS::memcpy ((char *) &this->log_msg_.host, + (char *) host_info->h_addr, + host_info->h_length); + + // This starts out initialized to all zeros! + server_ = ACE_INET_Addr (port, mcast_addr); + + if (logger_.subscribe (server_) == -1) + perror("can't subscribe to multicast group"), exit(1); + + // success. + return 0; +} + +// Send the message to a logger object. +// This wrapper fills in all the log_record info for you. +// uses iovector stuff to make contiguous header and message. + +int +Log_Wrapper::log_message (Log_Priority type, char *message) +{ + sequence_number_++; + + this->log_msg_.type = type; + this->log_msg_.time = time (0); + this->log_msg_.msg_length = ACE_OS::strlen(message)+1; + this->log_msg_.sequence_number = htonl(sequence_number_); + + iovec iovp[2]; + iovp[0].iov_base = reinterpret_cast<char*> (&log_msg_); + iovp[0].iov_len = sizeof (log_msg_); + iovp[1].iov_base = message; + iovp[1].iov_len = log_msg_.msg_length; + + logger_.send (iovp, 2); + + // success. + return 0; +} + diff --git a/ACE/examples/Reactor/Multicast/Log_Wrapper.h b/ACE/examples/Reactor/Multicast/Log_Wrapper.h new file mode 100644 index 00000000000..10458f706bc --- /dev/null +++ b/ACE/examples/Reactor/Multicast/Log_Wrapper.h @@ -0,0 +1,68 @@ +/* -*- C++ -*- */ +// $Id$ + +// log_wrapper.h + +#include "ace/Profile_Timer.h" + +#if !defined (ACE_LACKS_PRAGMA_ONCE) +# pragma once +#endif /* ACE_LACKS_PRAGMA_ONCE */ + +#include "ace/INET_Addr.h" +#include "ace/SOCK_Dgram_Mcast.h" + +#ifndef _LOG_WRAPPER_H +#define _LOG_WRAPPER_H + +class Log_Wrapper + // = TITLE + // Provide a wrapper around sending log messages via IP + // multicast. +{ +public: + Log_Wrapper (void); + ~Log_Wrapper (void); + + // = Types of logging messages. + enum Log_Priority + { + LM_MESSAGE, + LM_DEBUG, + LM_WARNING, + LM_ERROR, + LM_EMERG + }; + + int open (const int port, const char* mcast_addr); + // Subscribe to a given UDP multicast group + + int log_message (Log_Priority type, char *message); + // send a string to the logger + + // = Format of the logging record. + struct Log_Record + { + u_long sequence_number; + Log_Priority type; + long host; + long time; + long app_id; + long msg_length; + }; + +private: + ACE_INET_Addr server_; + // Server address where records are logged. + + u_long sequence_number_; + // Keep track of the sequence. + + Log_Record log_msg_; + // One record used for many log messages. + + ACE_SOCK_Dgram_Mcast logger_; + // A logger object. +}; + +#endif /* _LOG_WRAPPER_H */ diff --git a/ACE/examples/Reactor/Multicast/Makefile.am b/ACE/examples/Reactor/Multicast/Makefile.am new file mode 100644 index 00000000000..60d65e7ebe8 --- /dev/null +++ b/ACE/examples/Reactor/Multicast/Makefile.am @@ -0,0 +1,50 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +## Makefile.Reactor_Multicast_Client.am +noinst_PROGRAMS = client + +client_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +client_SOURCES = \ + Log_Wrapper.cpp \ + client.cpp \ + Log_Wrapper.h + +client_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.Reactor_Multicast_Server.am +noinst_PROGRAMS += server + +server_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +server_SOURCES = \ + Log_Wrapper.cpp \ + server.cpp \ + Log_Wrapper.h + +server_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/Multicast/README b/ACE/examples/Reactor/Multicast/README new file mode 100644 index 00000000000..85f64cc8120 --- /dev/null +++ b/ACE/examples/Reactor/Multicast/README @@ -0,0 +1,15 @@ +The following test illustrates the SOCK Mcast multicast wrappers in +conjunction with the Reactor. This test was written by Tim Harrison +(harrison@cs.wustl.edu). + +To run the server type: + +% server & + +It will wait for the first message sent to it and then read for 5 seconds. + +To run the client type any of these: + +% client -m max_message_size -i iterations +% client < <filename> +% client diff --git a/ACE/examples/Reactor/Multicast/Reactor_Multicast.mpc b/ACE/examples/Reactor/Multicast/Reactor_Multicast.mpc new file mode 100644 index 00000000000..a15c53340e4 --- /dev/null +++ b/ACE/examples/Reactor/Multicast/Reactor_Multicast.mpc @@ -0,0 +1,17 @@ +// -*- MPC -*- +// $Id$ + +project(*client) : aceexe { + exename = client + Source_Files { + client.cpp + Log_Wrapper.cpp + } +} +project(*server) : aceexe { + exename = server + Source_Files { + server.cpp + Log_Wrapper.cpp + } +} diff --git a/ACE/examples/Reactor/Multicast/client.cpp b/ACE/examples/Reactor/Multicast/client.cpp new file mode 100644 index 00000000000..25b18c2ae6c --- /dev/null +++ b/ACE/examples/Reactor/Multicast/client.cpp @@ -0,0 +1,126 @@ +// $Id$ + +// This program reads in messages from stdin and sends them to a +// Log_Wrapper. + +#include "ace/OS_main.h" +#include "ace/OS_NS_stdlib.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_Memory.h" +#include "ace/Get_Opt.h" +#include "ace/Log_Msg.h" +#include "Log_Wrapper.h" + +ACE_RCSID(Multicast, client, "$Id$") + +// Multi-cast address. +static const char *MCAST_ADDR = ACE_DEFAULT_MULTICAST_ADDR; + +// UDP port. +static const int UDP_PORT = ACE_DEFAULT_MULTICAST_PORT; + +// Maximum message size. +static int max_message_size = BUFSIZ; + +// Number of times to send message of max_message_size. +static int iterations = 0; + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_LOG_MSG->open (argv[0]); + + // Start at argv[1] + ACE_Get_Opt getopt (argc, argv, ACE_TEXT("m:ui:"), 1); + + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + case 'm': + max_message_size = ACE_OS::atoi (getopt.opt_arg ()) * BUFSIZ; + break; + case 'i': + iterations = ACE_OS::atoi (getopt.opt_arg ()); + break; + case 'u': + // usage fallthrough + default: + ACE_ERROR ((LM_ERROR, + "%n: -m max_message_size (in k) -i iterations\n%a", + 1)); + /* NOTREACHED */ + } +} + +int +ACE_TMAIN (int argc, ACE_TCHAR **argv) +{ + int user_prompt; + + parse_args (argc,argv); + + ACE_DEBUG ((LM_DEBUG, "max buffer size = %d\n", max_message_size)); + + // Instantiate a log wrapper for logging + Log_Wrapper log; + + // Make a connection to a logger. + if (log.open (UDP_PORT, MCAST_ADDR) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n" "open"), -1); + + char *buf; + ACE_NEW_RETURN (buf, char[max_message_size], -1); + + // If -i has been specified, send max_message_size messages + // iterations number of times. + if (iterations) + { + ACE_OS::memset (buf, 1, max_message_size); + + while (iterations--) + if (log.log_message (Log_Wrapper::LM_DEBUG, buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n" "log"), -1); + } + + // otherwise, a file has been redirected, or give prompts + else + { + // If a file has been redirected, don't activate user prompts. + if (ACE_OS::isatty (0)) + user_prompt = 1; + else + user_prompt = 0; + + // Continually read messages from stdin and log them. + + for (int count = 1;;) + { + if (user_prompt) + ACE_DEBUG ((LM_DEBUG, "\nEnter message ('Q':quit):\n")); + + ssize_t nbytes = ACE_OS::read (ACE_STDIN, buf, max_message_size); + + if (nbytes <= 0) + break; // End of file or error. + buf[nbytes - 1] = '\0'; + + // Quitting? + if (user_prompt) + { + if (buf[0] == 'Q' || buf[0] == 'q') + break; + } + else // Keep from overrunning the receiver. + ACE_OS::sleep (1); + + // Send the message to the logger. + if (log.log_message (Log_Wrapper::LM_DEBUG, buf) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n" "log_message"), -1); + ACE_DEBUG ((LM_DEBUG, "finished sending message %d\n", count++)); + } + } + + ACE_DEBUG ((LM_DEBUG, "Client done.\n")); + return 0; +} diff --git a/ACE/examples/Reactor/Multicast/server.cpp b/ACE/examples/Reactor/Multicast/server.cpp new file mode 100644 index 00000000000..65e39b97d1a --- /dev/null +++ b/ACE/examples/Reactor/Multicast/server.cpp @@ -0,0 +1,247 @@ +// $Id$ + +// server.cpp (written by Tim Harrison) + +// Listens to multicast address for client log messages. Prints +// Mbits/sec received from client. + +#include "ace/OS_main.h" +#include "ace/SOCK_Dgram.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Dgram_Mcast.h" +#include "ace/Reactor.h" +#include "ace/Log_Msg.h" +#include "Log_Wrapper.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_NS_unistd.h" +#include "ace/os_include/os_netdb.h" + +ACE_RCSID(Multicast, server, "$Id$") + +#if defined (ACE_HAS_IP_MULTICAST) +class Server_Events : public ACE_Event_Handler +{ +public: + Server_Events (u_short port, + const char *mcast_addr, + long time_interval = 0); + ~Server_Events (void); + + virtual int handle_input (ACE_HANDLE fd); + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg); + + virtual ACE_HANDLE get_handle (void) const; + + ACE_Time_Value *wait_time (void); + +private: + char *message_; + Log_Wrapper::Log_Record *log_record_; + char buf_[4 * BUFSIZ]; + char hostname_[MAXHOSTNAMELEN]; + + int initialized_; + int count_; + long interval_; + // time interval to log messages + + ACE_Time_Value *how_long_; + ACE_Reactor *reactor_; + ACE_SOCK_Dgram_Mcast mcast_dgram_; + ACE_INET_Addr remote_addr_; + ACE_INET_Addr mcast_addr_; + + // = statistics on messages received + double total_bytes_received_; + int total_messages_received_; + int last_sequence_number_; +}; + +static const char MCAST_ADDR[] = ACE_DEFAULT_MULTICAST_ADDR; +static const int UDP_PORT = ACE_DEFAULT_MULTICAST_PORT; +static const int DURATION = 5; + +ACE_HANDLE +Server_Events::get_handle (void) const +{ + return this->mcast_dgram_.get_handle (); +} + +ACE_Time_Value * +Server_Events::wait_time (void) +{ + return this->how_long_; +} + +Server_Events::Server_Events (u_short port, + const char *mcast_addr, + long time_interval) + : initialized_ (0), + count_ (1), + interval_ (time_interval), + mcast_addr_ (port, mcast_addr), + total_bytes_received_ (0) +{ + // Use ACE_SOCK_Dgram_Mcast factory to subscribe to multicast group. + + if (ACE_OS::hostname (this->hostname_, + MAXHOSTNAMELEN) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "hostname")); + + else if (this->mcast_dgram_.subscribe (this->mcast_addr_) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "subscribe")); + else + { + // Point to NULL so that we block in the beginning. + this->how_long_ = 0; + + this->log_record_ = (Log_Wrapper::Log_Record *) &buf_; + this->message_ = &buf_[sizeof (Log_Wrapper::Log_Record)]; + } +} + +// A destructor that emacs refuses to color blue ;-) + +Server_Events::~Server_Events (void) +{ + this->mcast_dgram_.unsubscribe (); + + ACE_DEBUG ((LM_DEBUG, + "total bytes received = %d after %d second\n", + this->total_bytes_received_, + this->interval_)); + + ACE_DEBUG ((LM_DEBUG, + "Mbits/sec = %.2f\n", + (float) (total_bytes_received_ * 8 / (float) (1024*1024*interval_)))); + + ACE_DEBUG ((LM_DEBUG, + "last sequence number = %d\ntotal messages received = %d\ndiff = %d\n", + this->last_sequence_number_, + this->total_messages_received_, + this->last_sequence_number_ - total_messages_received_)); +} + +int +Server_Events::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + ACE_DEBUG ((LM_DEBUG, "\t%d timeout%s occurred for %s.\n", + this->count_, + this->count_ == 1 ? "" : "s", + (char *) arg)); + + // Don't let the timeouts continue if there's no activity since + // otherwise we use up a lot of CPU time unnecessarily. + if (this->count_ == 5) + { + reactor ()->cancel_timer (this); + this->initialized_ = 0; + + ACE_DEBUG ((LM_DEBUG, + "\tcancelled timeout for %s to avoid busy waiting.\n", + (char *) arg)); + } + + this->count_++; + return 0; +} + +int +Server_Events::handle_input (ACE_HANDLE) +{ + // Receive message from multicast group. + iovec iovp[2]; + iovp[0].iov_base = buf_; + iovp[0].iov_len = sizeof (log_record_); + iovp[1].iov_base = &buf_[sizeof (log_record_)]; + iovp[1].iov_len = 4 * BUFSIZ - sizeof (log_record_); + + ssize_t retcode = + this->mcast_dgram_.recv (iovp, + 2, + this->remote_addr_); + if (retcode != -1) + { + total_messages_received_++; + total_bytes_received_ += retcode; + last_sequence_number_ = + ntohl (log_record_->sequence_number); + + for (char *message_end = this->message_ + ACE_OS::strlen (this->message_) - 1; + ACE_OS::strchr ("\r\n \t", *message_end) != 0; + ) + { + *message_end-- = '\0'; + if (message_end == this->message_) + break; + } + + ACE_DEBUG ((LM_DEBUG, + "sequence number = %d\n", + last_sequence_number_)); + ACE_DEBUG ((LM_DEBUG, + "message = '%s'\n", + this->message_)); + + if (this->initialized_ == 0) + { + // Restart the timer since we've received events again. + if (reactor()->schedule_timer (this, + (void *) this->hostname_, + ACE_Time_Value::zero, + ACE_Time_Value (DURATION)) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "schedule_timer"), + -1); + this->initialized_ = 1; + } + + this->count_ = 1; + return 0; + } + else + return -1; +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + // Instantiate a server which will receive messages for DURATION + // seconds. + Server_Events server_events (UDP_PORT, + MCAST_ADDR, + DURATION); + // Instance of the ACE_Reactor. + ACE_Reactor reactor; + + if (reactor.register_handler (&server_events, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n%a", + "register_handler", + 1)); + + ACE_DEBUG ((LM_DEBUG, + "starting up server\n")); + + for (;;) + reactor.handle_events (server_events.wait_time ()); + + ACE_NOTREACHED (return 0;) +} +#else +int +main (int, char *argv[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "error: %s must be run on a platform that support IP multicast\n", + argv[0]), -1); +} +#endif /* ACE_HAS_IP_MULTICAST */ diff --git a/ACE/examples/Reactor/Ntalker/.cvsignore b/ACE/examples/Reactor/Ntalker/.cvsignore new file mode 100644 index 00000000000..a9350d173bf --- /dev/null +++ b/ACE/examples/Reactor/Ntalker/.cvsignore @@ -0,0 +1,2 @@ +ntalker +ntalker diff --git a/ACE/examples/Reactor/Ntalker/Makefile.am b/ACE/examples/Reactor/Ntalker/Makefile.am new file mode 100644 index 00000000000..6efc3521a32 --- /dev/null +++ b/ACE/examples/Reactor/Ntalker/Makefile.am @@ -0,0 +1,33 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +## Makefile.Reactor_Ntalker.am +noinst_PROGRAMS = ntalker + +ntalker_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +ntalker_SOURCES = \ + ntalker.cpp + +ntalker_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/Ntalker/README b/ACE/examples/Reactor/Ntalker/README new file mode 100644 index 00000000000..191cef9256f --- /dev/null +++ b/ACE/examples/Reactor/Ntalker/README @@ -0,0 +1,17 @@ +This test program illustrates how the ACE datagram multicast feature +works. To run the test simply do the following on multiple machines: + +# Machine 1 +% ./ntalker + +# Machine 2 +% ./ntalker + +# Machine 3 +% ./ntalker + +Then, on one (or all) of the machines, type input into the keyboard. +This input will be multicast to all the machines using IP multicast +via the ACE_SOCK_Dgram_Mcast wrapper. + +When you want to shut down the sender, just type ^D. diff --git a/ACE/examples/Reactor/Ntalker/Reactor_Ntalker.mpc b/ACE/examples/Reactor/Ntalker/Reactor_Ntalker.mpc new file mode 100644 index 00000000000..ef2fb84d9a0 --- /dev/null +++ b/ACE/examples/Reactor/Ntalker/Reactor_Ntalker.mpc @@ -0,0 +1,6 @@ +// -*- MPC -*- +// $Id$ + +project : aceexe { + exename = ntalker +} diff --git a/ACE/examples/Reactor/Ntalker/ntalker.cpp b/ACE/examples/Reactor/Ntalker/ntalker.cpp new file mode 100644 index 00000000000..80873ead1a9 --- /dev/null +++ b/ACE/examples/Reactor/Ntalker/ntalker.cpp @@ -0,0 +1,231 @@ +// $Id$ + +// Listens to multicast address. After first message received, will +// listen for 5 more seconds. Prints Mbits/sec received from client. + +#include "ace/OS_main.h" +#include "ace/OS_NS_unistd.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Dgram_Mcast.h" +#include "ace/Reactor.h" +#include "ace/Get_Opt.h" +#include "ace/Thread_Manager.h" +#include "ace/Service_Config.h" + +ACE_RCSID(Ntalker, ntalker, "$Id$") + +#if defined (ACE_HAS_IP_MULTICAST) +// Network interface to subscribe to. This is hardware specific. use +// netstat(1M) to find whether your interface is le0 or ie0 + +static const ACE_TCHAR *INTERFACE = 0; +static const char *MCAST_ADDR = ACE_DEFAULT_MULTICAST_ADDR; +static const u_short UDP_PORT = ACE_DEFAULT_MULTICAST_PORT; + +class Handler : public ACE_Event_Handler +{ + // = TITLE + // Handle both multicast and stdin events. +public: + // = Initialization and termination methods. + Handler (u_short udp_port, + const char *ip_addr, + const ACE_TCHAR *a_interface, + ACE_Reactor & ); + // Constructor. + + ~Handler (void); + // Destructor. + + // Event demuxer hooks. + virtual int handle_input (ACE_HANDLE); + virtual int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); + virtual ACE_HANDLE get_handle (void) const; + +private: + ACE_SOCK_Dgram_Mcast mcast_; + // Multicast wrapper. +}; + +ACE_HANDLE +Handler::get_handle (void) const +{ + return this->mcast_.get_handle (); +} + +int +Handler::handle_input (ACE_HANDLE h) +{ + char buf[BUFSIZ]; + + if (h == ACE_STDIN) + { + ssize_t result = ACE_OS::read (h, buf, BUFSIZ); + + if (result > 0) + { + if (this->mcast_.send (buf, result) != result) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "send error"), + -1); + return 0; + } + else if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "can't read from STDIN"), + -1); + else // result == 0 + { + ACE_Reactor::end_event_loop (); + return -1; + } + } + else + { + ACE_INET_Addr remote_addr; + + // Receive message from multicast group. + ssize_t result = this->mcast_.recv (buf, + sizeof buf, + remote_addr); + + if (result != -1) + { + ACE_DEBUG ((LM_DEBUG, + "received datagram from host %s on port %d bytes = %d\n", + remote_addr.get_host_name (), + remote_addr.get_port_number (), + result)); + ACE_OS::write (ACE_STDERR, buf, result); + ACE_DEBUG ((LM_DEBUG, + "\n")); + return 0; + } + + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "something amiss"), + -1); + } +} + +int +Handler::handle_close (ACE_HANDLE h, ACE_Reactor_Mask) +{ + if (h == ACE_STDIN) + { + ACE_DEBUG ((LM_DEBUG, + "STDIN_Events handle removed from reactor.\n")); + if (ACE_Reactor::instance ()->remove_handler + (this, ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "remove_handler"), + -1); + } + else + ACE_DEBUG ((LM_DEBUG, + "Mcast_Events handle removed from reactor.\n")); + return 0; +} + +Handler::~Handler (void) +{ + if (this->mcast_.unsubscribe () == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "unsubscribe fails")); +} + +Handler::Handler (u_short udp_port, + const char *ip_addr, + const ACE_TCHAR *a_interface, + ACE_Reactor &reactor) +{ + // Create multicast address to listen on. + + ACE_INET_Addr sockmc_addr (udp_port, ip_addr); + + // subscribe to multicast group. + + if (this->mcast_.subscribe (sockmc_addr, 1, a_interface) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "can't subscribe to multicast group")); + } + // Disable loopbacks. + // if (this->mcast_.set_option (IP_MULTICAST_LOOP, 0) == -1 ) + // ACE_OS::perror (" can't disable loopbacks " ), ACE_OS::exit (1); + + // Register callbacks with the ACE_Reactor. + else if (reactor.register_handler (this->mcast_.get_handle (), + this, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "can't register with Reactor\n")); + // Register the STDIN handler. + else if (ACE_Event_Handler::register_stdin_handler (this, + ACE_Reactor::instance (), + ACE_Thread_Manager::instance ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "register_stdin_handler")); +} + +static void +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("i:u")); + + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 'i': + INTERFACE = get_opt.opt_arg (); + break; + case 'u': + // Usage fallthrough. + default: + ACE_DEBUG ((LM_DEBUG, + "%s -i interface\n", + argv[0])); + ACE_OS::exit (1); + } +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + parse_args (argc, argv); + + Handler handler (UDP_PORT, + MCAST_ADDR, + INTERFACE, + *ACE_Reactor::instance ()); + + // Run the event loop. + ACE_Reactor::run_event_loop (); + + ACE_DEBUG ((LM_DEBUG, + "talker Done.\n")); + return 0; +} + +#else +int +ACE_TMAIN (int, ACE_TCHAR *argv[]) +{ + ACE_ERROR_RETURN ((LM_ERROR, + "error: %s must be run on a platform that support IP multicast\n", + argv[0]), + 0); +} +#endif /* ACE_HAS_IP_MULTICAST */ + diff --git a/ACE/examples/Reactor/Proactor/.cvsignore b/ACE/examples/Reactor/Proactor/.cvsignore new file mode 100644 index 00000000000..34179361b75 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/.cvsignore @@ -0,0 +1,7 @@ +test_cancel +test_end_event_loop +test_multiple_loops +test_post_completions +test_proactor +test_timeout +test_udp_proactor diff --git a/ACE/examples/Reactor/Proactor/Aio_Platform_Test_C.cpp b/ACE/examples/Reactor/Proactor/Aio_Platform_Test_C.cpp new file mode 100644 index 00000000000..be720fdef40 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/Aio_Platform_Test_C.cpp @@ -0,0 +1,137 @@ +// $Id$ +// ============================================================================ +// +// = FILENAME +// aio_platform_test_c.cpp +// +// = DESCRITPTION +// Testing the platform for POSIX Asynchronous I/O. This is the C +// version of the $ACE_ROOT/tests/Aio_Platform_Test.cpp. Useful +// to send bug reports. +// +// = AUTHOR +// Programming for the Real World. Bill O. GallMeister. +// Modified by Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + + +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include <limits.h> + +#include <aio.h> + +int do_sysconf (void); +int have_asynchio (void); + +static int file_handle = -1; +char mb1 [BUFSIZ + 1]; +char mb2 [BUFSIZ + 1]; +aiocb aiocb1, aiocb2; +sigset_t completion_signal; + +// For testing the <aio> stuff. +int test_aio_calls (void); +int issue_aio_calls (void); +int query_aio_completions (void); +int setup_signal_delivery (void); +int do_sysconf (void); +int have_asynchio (void); + +int +do_sysconf (void) +{ + // Call sysconf to find out runtime values. + errno = 0; +#if defined (_SC_LISTIO_AIO_MAX) + printf ("Runtime value of LISTIO_AIO_MAX is %d, errno = %d\n", + sysconf(_SC_LISTIO_AIO_MAX), + errno); +#else + printf ("Runtime value of AIO_LISTIO_MAX is %d, errno = %d\n", + sysconf(_SC_AIO_LISTIO_MAX), + errno); +#endif + + errno = 0; + printf ("Runtime value of AIO_MAX is %d, errno = %d\n", + sysconf (_SC_AIO_MAX), + errno); + + errno = 0; + printf ("Runtime value of _POSIX_ASYNCHRONOUS_IO is %d, errno = %d\n", + sysconf (_SC_ASYNCHRONOUS_IO), + errno); + + errno = 0; + printf ("Runtime value of _POSIX_REALTIME_SIGNALS is %d, errno = %d\n", + sysconf (_SC_REALTIME_SIGNALS), + errno); + + errno = 0; + printf ("Runtime value of RTSIG_MAX %d, Errno = %d\n", + sysconf (_SC_RTSIG_MAX), + errno); + + errno = 0; + printf ("Runtime value of SIGQUEUE_MAX %d, Errno = %d\n", + sysconf (_SC_SIGQUEUE_MAX), + errno); + return 0; +} + +int +have_asynchio (void) +{ +#if defined (_POSIX_ASYNCHRONOUS_IO) + // POSIX Asynch IO is present in this system. +#if defined (_POSIX_ASYNC_IO) + // If this is defined and it is not -1, POSIX_ASYNCH is supported + // everywhere in the system. +#if _POSIX_ASYNC_IO == -1 + printf ("_POSIX_ASYNC_IO = -1.. ASYNCH IO NOT supported at all\n"); + return -1; +#else /* Not _POSIX_ASYNC_IO == -1 */ + printf ("_POSIX_ASYNC_IO = %d\n ASYNCH IO is supported FULLY\n", + _POSIX_ASYNC_IO); +#endif /* _POSIX_ASYNC_IO == -1 */ + +#else /* Not defined _POSIX_ASYNC_IO */ + printf ("_POSIX_ASYNC_IO is not defined.\n"); + printf ("AIO might *not* be supported on some paths\n"); +#endif /* _POSIX_ASYNC_IO */ + + // System defined POSIX Values. + printf ("System claims to have POSIX_ASYNCHRONOUS_IO\n"); + + printf ("_POSIX_AIO_LISTIO_MAX = %d\n", _POSIX_AIO_LISTIO_MAX); + printf ("_POSIX_AIO_MAX = %d\n", _POSIX_AIO_MAX); + + // Check and print the run time values. + do_sysconf (); + + return 0; + +#else /* Not _POSIX_ASYNCHRONOUS_IO */ + printf ("No support._POSIX_ASYNCHRONOUS_IO itself is not defined\n"); + return -1; +#endif /* _POSIX_ASYNCHRONOUS_IO */ +} + +int +main (int, char *[]) +{ + if (have_asynchio () == 0) + printf ("Test successful\n"); + else + printf ("Test not successful\n"); + return 0; +} diff --git a/ACE/examples/Reactor/Proactor/Makefile.am b/ACE/examples/Reactor/Proactor/Makefile.am new file mode 100644 index 00000000000..7f1bc4b8a57 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/Makefile.am @@ -0,0 +1,153 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.Proactor_Cancel.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_cancel + +test_cancel_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_cancel_SOURCES = \ + test_cancel.cpp \ + test_cancel.h + +test_cancel_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_End_Event_Loops.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_end_event_loop + +test_end_event_loop_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_end_event_loop_SOURCES = \ + test_end_event_loop.cpp \ + test_cancel.h \ + test_proactor.h + +test_end_event_loop_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Multiple_Loops.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_multiple_loops + +test_multiple_loops_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_multiple_loops_SOURCES = \ + test_multiple_loops.cpp \ + test_cancel.h \ + test_proactor.h + +test_multiple_loops_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Post_Completions.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_post_completions + +test_post_completions_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_post_completions_SOURCES = \ + post_completions.cpp \ + test_cancel.h \ + test_proactor.h + +test_post_completions_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Proactor.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_proactor + +test_proactor_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_proactor_SOURCES = \ + test_proactor.cpp \ + test_proactor.h + +test_proactor_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Timeout.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_timeout + +test_timeout_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_timeout_SOURCES = \ + test_timeout.cpp \ + test_cancel.h \ + test_proactor.h + +test_timeout_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Makefile.Proactor_Udp_Proactor.am + +if !BUILD_ACE_FOR_TAO +noinst_PROGRAMS += test_udp_proactor + +test_udp_proactor_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +test_udp_proactor_SOURCES = \ + test_udp_proactor.cpp \ + test_cancel.h \ + test_proactor.h + +test_udp_proactor_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_ACE_FOR_TAO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/Proactor/Proactor.mpc b/ACE/examples/Reactor/Proactor/Proactor.mpc new file mode 100644 index 00000000000..c2c52207ca1 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/Proactor.mpc @@ -0,0 +1,59 @@ +// -*- MPC -*- +// $Id$ + +project(*cancel) : aceexe { + avoids += ace_for_tao + exename = test_cancel + Source_Files { + test_cancel.cpp + } +} + +project(*end_event_loops) : aceexe { + avoids += ace_for_tao + exename = test_end_event_loop + Source_Files { + test_end_event_loop.cpp + } +} + +project(*multiple_loops) : aceexe { + avoids += ace_for_tao + exename = test_multiple_loops + Source_Files { + test_multiple_loops.cpp + } +} + +project(*post_completions) : aceexe { + avoids += ace_for_tao + exename = test_post_completions + Source_Files { + post_completions.cpp + } +} + +project(*proactor) : aceexe { + avoids += ace_for_tao + exename = test_proactor + Source_Files { + test_proactor.cpp + } +} + +project(*timeout) : aceexe { + avoids += ace_for_tao + exename = test_timeout + Source_Files { + test_timeout.cpp + } +} + +project(*udp_proactor) : aceexe { + avoids += ace_for_tao + exename = test_udp_proactor + Source_Files { + test_udp_proactor.cpp + } +} + diff --git a/ACE/examples/Reactor/Proactor/README b/ACE/examples/Reactor/Proactor/README new file mode 100644 index 00000000000..29f2a0b1832 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/README @@ -0,0 +1,75 @@ +$Id$ + +This README file lists all the example applications for the Proactor framework. + +Test/Example Applications for Proactor: +========================================= + +The following tests are available. + +o $ACE_ROOT/tests/Aio_Platform_Test.cpp : Tests basic limits + pertaining to the POSIX features + +o $ACE_ROOT/examples/Reactor/Proactor/test_aiocb.cpp : + This is a C++ program for testing the AIOCB (AIO Control + Blocks) based completion approach which uses <aio_suspend> for + completion querying. + +o $ACE_ROOT/examples/Reactor/Proactor/test_aiosig.cpp : This is a + C++ program for testing the Signal based completion approach + that uses <sigtimedwait> for completion querying. + +o $ACE_ROOT/examples/Reactor/Proactor/test_aiocb_ace.cpp: Portable + version of test_aiocb.cpp. (Same as test_aiocb.cpp, but uses + ACE_DEBUGs instead of printf's and ACE_Message_Blocks instead + of char*'s. + +o $ACE_ROOT/examples/Reactor/Proactor/test_aiosig_ace.cpp: Portable + version of test_aiosig.cpp. (Same as test_aiosig.cpp, but uses + ACE_DEBUGs instead of printf's and ACE_Message_Blocks instead + of char*'s. + +o test_proactor.cpp (with ACE_POSIX_AIOCB_Proactor) : Test for + ACE_Proactor which uses AIOCB (AIO Control Blocks) based + completions strategy Proactor. (#define + ACE_POSIX_AIOCB_PROACTOR in the config file, but this is the + default option) + +o test_proactor.cpp (with ACE_POSIX_SIG_Proactor) : Test for + ACE_Proactor which uses real time signal based completion + strategy proactor. (#define ACE_POSIX_SIG_PROACTOR in the + config file) + +o test_multiple_loops.cpp : This example application shows how + to write programs that combine the Proactor and Reactor event + loops. This is possible only on WIN32 platform. + +o test_timeout.cpp : Multithreaded application testing the Timers + mechanism of the Proactor. + +o test_timeout_st.cpp : Single-threaded version of test_timeout.cpp. + +o post_completions.cpp : Tests the completion posting mechanism of + the Proactor. + +o test_end_event_loop.cpp : Tests the event loop mechanism of the + Proactor. + +o test_cancel.cpp : Tests <cancel> interface of the + Asynch_Operation class. + +Behavior of POSIX AIO of various platforms: +========================================== + +Sun 5.6 : POSIX4 Real-Time signals implementation is broken in + this platform. + Only POSIX AIOCB Proactor works in this platform. + Therefore, it is not possible to use multiple threads + with in the framework. + +Sun 5.7 : AIOCB and SIG Proactors work fine. + +LynxOS 3.0.0 : <pthread_sigmask> is not available in this + platform. So, only AIOCB Proactor works here. + + diff --git a/ACE/examples/Reactor/Proactor/post_completions.cpp b/ACE/examples/Reactor/Proactor/post_completions.cpp new file mode 100644 index 00000000000..e6545241953 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/post_completions.cpp @@ -0,0 +1,306 @@ +// $Id$ +// ============================================================================ +// +// = FILENAME +// post_completions.cpp +// +// = DESCRITPTION +// This program demonstrates how to post fake completions to The +// Proactor. It also shows the how to specify the particular +// real-time signals to post completions. The Real-time signal +// based completion strategy is implemented with +// ACE_POSIX_SIG_PROACTOR. +// (So, it can be used only if both ACE_HAS_AIO_CALLS and +// ACE_HAS_POSIX_REALTIME_SIGNALS are defined.) +// Since it is faking results, you have to pay by knowing and +// using platform-specific implementation objects for Asynchronous +// Result classes. +// This example shows using an arbitrary result class for faking +// completions. You can also use the predefined Result classes for +// faking. The factory methods in the Proactor class create the +// Result objects. +// +// = COMPILATION +// make +// +// = RUN +// ./post_completions +// +// = AUTHOR +// Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + +#include "ace/OS_NS_unistd.h" +#include "ace/OS_main.h" +#include "ace/Proactor.h" +#include "ace/Task.h" +#include "ace/WIN32_Proactor.h" +#include "ace/POSIX_Proactor.h" +#include "ace/Atomic_Op.h" +#include "ace/Thread_Mutex.h" + +// Keep track of how many completions are still expected. +static ACE_Atomic_Op <ACE_SYNCH_MUTEX, size_t> Completions_To_Go; + + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \ + defined (ACE_HAS_AIO_CALLS) +// This only works on Win32 platforms and on Unix platforms supporting +// POSIX aio calls. + +#if defined (ACE_HAS_AIO_CALLS) +#define RESULT_CLASS ACE_POSIX_Asynch_Result +#elif defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) +#define RESULT_CLASS ACE_WIN32_Asynch_Result +#endif /* ACE_HAS_AIO_CALLS */ + +class My_Result : public RESULT_CLASS +{ + // = TITLE + // + // Result Object that we will post to the Proactor. + // + // = DESCRIPTION + // + +public: + My_Result (ACE_Handler &handler, + const void *act, + int signal_number, + size_t sequence_number) + : RESULT_CLASS (handler.proxy (), + act, + ACE_INVALID_HANDLE, + 0, // Offset + 0, // OffsetHigh + 0, // Priority + signal_number), + sequence_number_ (sequence_number) + {} + // Constructor. + + virtual ~My_Result (void) + {} + // Destructor. + + void complete (size_t, + int success, + const void *completion_key, + u_long error) + // This is the method that will be called by the Proactor for + // dispatching the completion. This method generally calls one of + // the call back hood methods defined in the ACE_Handler + // class. But, we will just handle the completions here. + { + this->success_ = success; + this->completion_key_ = completion_key; + this->error_ = error; + + size_t to_go = --Completions_To_Go; + + // Print the completion details. + ACE_DEBUG ((LM_DEBUG, + "(%t) Completion sequence number %d, success : %d, error : %d, signal_number : %d, %u more to go\n", + this->sequence_number_, + this->success_, + this->error_, + this->signal_number (), + to_go)); + + // Sleep for a while. + ACE_OS::sleep (4); + } + +private: + size_t sequence_number_; + // Sequence number for the result object. +}; + +class My_Handler : public ACE_Handler +{ + // = TITLE + // + // Handler class for faked completions. + // + // = DESCRIPTION + // + +public: + My_Handler (void) {} + // Constructor. + + virtual ~My_Handler (void) {} + // Destructor. +}; + +class My_Task: public ACE_Task <ACE_NULL_SYNCH> +{ + // = TITLE + // + // Contains thread functions which execute event loops. Each + // thread waits for a different signal. + // +public: + My_Task (void) {} + // Constructor. + + virtual ~My_Task (void) {} + // Destructor. + + int open (void *proactor) + { + // Store the proactor. + this->proactor_ = (ACE_Proactor *) proactor; + + // Activate the Task. + this->activate (THR_NEW_LWP, 5); + return 0; + } + + int svc (void) + { + // Handle events for 13 seconds. + ACE_Time_Value run_time (13); + + ACE_DEBUG ((LM_DEBUG, "(%t):Starting svc routine\n")); + + if (this->proactor_->handle_events (run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t):%p.\n", "Worker::svc"), -1); + + ACE_DEBUG ((LM_DEBUG, "(%t) work complete\n")); + + return 0; + } + +private: + ACE_Proactor *proactor_; + // Proactor for this task. +}; + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_DEBUG ((LM_DEBUG, + "(%P | %t):Test starts \n")); + + // = Get two POSIX_SIG_Proactors, one with SIGRTMIN and one with + // SIGRTMAX. + + ACE_Proactor proactor1; + // Proactor1. SIGRTMIN Proactor. (default). + + // = Proactor2. SIGRTMAX Proactor. +#if defined (ACE_HAS_AIO_CALLS) && defined (ACE_HAS_POSIX_REALTIME_SIGNALS) + + ACE_DEBUG ((LM_DEBUG, "Using ACE_POSIX_SIG_Proactor\n")); + + sigset_t signal_set; + // Signal set that we want to mask. + + // Clear the signal set. + if (sigemptyset (&signal_set) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Error:%p\n", + "sigemptyset failed"), + 1); + + // Add the SIGRTMAX to the signal set. + if (sigaddset (&signal_set, ACE_SIGRTMAX) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Error:%p\n", + "sigaddset failed"), + 1); + + // Make the POSIX Proactor. + ACE_POSIX_SIG_Proactor posix_proactor (signal_set); + // Get the Proactor interface out of it. + ACE_Proactor proactor2 (&posix_proactor); +#else /* ACE_HAS_AIO_CALLS && ACE_HAS_POSIX_REALTIME_SIGNALS */ + ACE_Proactor proactor2; +#endif /* ACE_HAS_AIO_CALLS && ACE_HAS_POSIX_REALTIME_SIGNALS */ + + // = Create Tasks. One pool of threads to handle completions on + // SIGRTMIN and the other one to handle completions on SIGRTMAX. + My_Task task1, task2; + task1.open (&proactor1); + task2.open (&proactor2); + + // Handler for completions. + My_Handler handler; + + // = Create a few MyResult objects and post them to Proactor. + const size_t NrCompletions (10); + My_Result *result_objects [NrCompletions]; + int signal_number = ACE_SIGRTMAX; + size_t ri = 0; + + Completions_To_Go = NrCompletions; + + // Creation. + for (ri = 0; ri < NrCompletions; ri++) + { + // Use RTMIN and RTMAX proactor alternatively, to post + // completions. + if (ri % 2) + signal_number = ACE_SIGRTMIN; + else + signal_number = ACE_SIGRTMAX; + // Create the result. + ACE_NEW_RETURN (result_objects [ri], + My_Result (handler, + 0, + signal_number, + ri), + 1); + } + ACE_OS::sleep(5); + // Post all the result objects. + ACE_Proactor *proactor; + for (ri = 0; ri < NrCompletions; ri++) + { + // Use RTMIN and RTMAX Proactor alternatively, to post + // completions. + if (ri % 2) + proactor = &proactor1; + else + proactor = &proactor2; + if (result_objects [ri]->post_completion (proactor->implementation ()) + == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "Test failed\n"), + 1); + } + + ACE_Thread_Manager::instance ()->wait (); + + int status = 0; + size_t to_go = Completions_To_Go.value (); + if (size_t (0) != to_go) + { + ACE_ERROR ((LM_ERROR, + "Fail! Expected all completions to finish but %u to go\n", + to_go)); + status = 1; + } + + ACE_DEBUG ((LM_DEBUG, + "(%P | %t):Test ends\n")); + return status; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ + +int +main (int, char *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example cannot work with AIOCB_Proactor.\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ + diff --git a/ACE/examples/Reactor/Proactor/simple_test_proactor.cpp b/ACE/examples/Reactor/Proactor/simple_test_proactor.cpp new file mode 100644 index 00000000000..1f4557d7df5 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/simple_test_proactor.cpp @@ -0,0 +1,269 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// simple_test_proactor.cpp +// +// = DESCRIPTION +// Very simple version of test_proactor.cpp. +// +// = AUTHOR +// Alexander Babu Arulanthu (alex@cs.wustl.edu) +// +// ============================================================================ + +#include "ace/Service_Config.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/Asynch_IO_Impl.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" +#include "ace/OS_main.h" + +ACE_RCSID(Proactor, test_proactor, "simple_test_proactor.cpp,v 1.1 1999/05/18 22:15:30 alex Exp") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms supporting + // POSIX aio calls. + +static ACE_TCHAR *file = ACE_TEXT("simple_test_proactor.cpp"); +static ACE_TCHAR *dump_file = ACE_TEXT("simple_output"); + +class Simple_Tester : public ACE_Handler +{ + // = TITLE + // + // Simple_Tester + // + // = DESCRIPTION + // + // The class will be created by main(). This class reads a block + // from the file and write that to the dump file. + +public: + Simple_Tester (void); + // Constructor. + + ~Simple_Tester (void); + + int open (void); + // Open the operations and initiate read from the file. + +protected: + // = These methods are called by the freamwork. + + virtual void handle_read_file (const ACE_Asynch_Read_File::Result &result); + // This is called when asynchronous reads from the socket complete. + + virtual void handle_write_file (const ACE_Asynch_Write_File::Result &result); + // This is called when asynchronous writes from the socket complete. + +private: + int initiate_read_file (void); + + ACE_Asynch_Read_File rf_; + // rf (read file): for writing from the file. + + ACE_Asynch_Write_File wf_; + // ws (write File): for writing to the file. + + ACE_HANDLE input_file_; + // File to read from. + + ACE_HANDLE dump_file_; + // File for dumping data. + + // u_long file_offset_; + // Current file offset + + // u_long file_size_; + // File size +}; + + +Simple_Tester::Simple_Tester (void) + : input_file_ (ACE_INVALID_HANDLE), + dump_file_ (ACE_INVALID_HANDLE) +{ +} + +Simple_Tester::~Simple_Tester (void) +{ + ACE_OS::close (this->input_file_); + ACE_OS::close (this->dump_file_); +} + + +int +Simple_Tester::open (void) +{ + // Initialize stuff + + // Open input file (in OVERLAPPED mode) + this->input_file_ = ACE_OS::open (file, + GENERIC_READ | FILE_FLAG_OVERLAPPED); + if (this->input_file_ == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_OS::open"), -1); + + // Open dump file (in OVERLAPPED mode) + this->dump_file_ = ACE_OS::open (dump_file, + O_CREAT | O_RDWR | O_TRUNC | FILE_FLAG_OVERLAPPED, + 0644); + if (this->dump_file_ == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_OS::open"), -1); + + // Open ACE_Asynch_Read_File + if (this->rf_.open (*this, this->input_file_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_Asynch_Read_File::open"), -1); + + // Open ACE_Asynch_Write_File + if (this->wf_.open (*this, this->dump_file_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_Asynch_Write_File::open"), -1); + + ACE_DEBUG ((LM_DEBUG, + "Simple_Tester::open: Files and Asynch Operations opened sucessfully\n")); + + + // Start an asynchronous read file + if (this->initiate_read_file () == -1) + return -1; + + return 0; +} + + +int +Simple_Tester::initiate_read_file (void) +{ + // Create Message_Block + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, ACE_Message_Block (BUFSIZ + 1), -1); + + // Inititiate an asynchronous read from the file + if (this->rf_.read (*mb, + mb->size () - 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_Asynch_Read_File::read"), -1); + + ACE_DEBUG ((LM_DEBUG, + "Simple_Tester:initiate_read_file: Asynch Read File issued sucessfully\n")); + + return 0; +} + +void +Simple_Tester::handle_read_file (const ACE_Asynch_Read_File::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, "handle_read_file called\n")); + + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + // Watch out if you need to enable this... the ACE_Log_Record::MAXLOGMSGLEN + // value controls to max length of a log record, and a large output + // buffer may smash it. +#if 0 + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", + "message_block", + result.message_block ().rd_ptr ())); +#endif /* 0 */ + + if (result.success ()) + { + // Read successful: write this to the file. + if (this->wf_.write (result.message_block (), + result.bytes_transferred ()) == -1) + { + ACE_ERROR ((LM_ERROR, "%p\n", "ACE_Asynch_Write_File::write")); + return; + } + } +} + +void +Simple_Tester::handle_write_file (const ACE_Asynch_Write_File::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, "handle_write_File called\n")); + + // Reset pointers + result.message_block ().rd_ptr (result.message_block ().rd_ptr () - result.bytes_transferred ()); + + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + // Watch out if you need to enable this... the ACE_Log_Record::MAXLOGMSGLEN + // value controls to max length of a log record, and a large output + // buffer may smash it. +#if 0 + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", + "message_block", + result.message_block ().rd_ptr ())); +#endif /* 0 */ + ACE_Proactor::end_event_loop (); +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("f:d:")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'f': + file = get_opt.opt_arg (); + break; + case 'd': + dump_file = get_opt.opt_arg (); + break; + default: + ACE_ERROR ((LM_ERROR, "%p.\n", + "usage :\n" + "-d <dumpfile>\n" + "-f <file>\n")); + return -1; + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + Simple_Tester Simple_Tester; + + if (Simple_Tester.open () == -1) + return -1; + + int success = 1; + + // dispatch events + success = !(ACE_Proactor::run_event_loop () == -1); + + return success ? 0 : 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_aiocb.cpp b/ACE/examples/Reactor/Proactor/test_aiocb.cpp new file mode 100644 index 00000000000..c9c0d280f1b --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_aiocb.cpp @@ -0,0 +1,239 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// proactor +// +// = FILENAME +// test_aiocb.cpp +// +// = DESCRIPTION +// Checkout $ACE_ROOT/examples/Reactor/Proactor/test_aiocb_ace.cpp, +// which is the ACE'ified version of this program. +// +// = COMPILE and RUN +// % CC -g -o test_aiocb -lrt test_aiocb.cpp +// % ./test_aiocb +// +// = AUTHOR +// Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ============================================================================ + +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <iostream.h> + +#include <aio.h> + +class Test_Aio +{ +public: + Test_Aio (void); + // Default constructor. + + int init (void); + // Initting the output file and the buffer. + + int do_aio (void); + // Doing the testing stuff. + + ~Test_Aio (void); + // Destructor. +private: + int out_fd_; + // Output file descriptor. + + struct aiocb *aiocb_write_; + // For writing to the file. + + struct aiocb *aiocb_read_; + // Reading stuff from the file. + + char *buffer_write_; + // The buffer to be written to the out_fd. + + char *buffer_read_; + // The buffer to be read back from the file. +}; + +Test_Aio::Test_Aio (void) + : aiocb_write_ (new struct aiocb), + aiocb_read_ (new struct aiocb), + buffer_write_ (0), + buffer_read_ (0) +{ +} + +Test_Aio::~Test_Aio (void) +{ + delete aiocb_write_; + delete aiocb_read_; + delete buffer_write_; + delete buffer_read_; +} + +// Init the output file and init the buffer. +int +Test_Aio::init (void) +{ + // Open the output file. + this->out_fd_ = open ("test_aio.log", O_RDWR | O_CREAT | O_TRUNC, 0666); + if (this->out_fd_ == 0) + { + cout << "Error : Opening file" << endl; + return -1; + } + + // Init the buffers. + this->buffer_write_ = strdup ("Welcome to the world of AIO... AIO Rules !!!"); + cout << "The buffer : " << this->buffer_write_ << endl; + this->buffer_read_ = new char [strlen (this->buffer_write_) + 1]; + return 0; +} + +// Set the necessary things for the AIO stuff. +// Write the buffer asynchly.hmm Disable signals. +// Go on aio_suspend. Wait for completion. +// Print out the result. +int +Test_Aio::do_aio (void) +{ + // = Write to the file. + + // Setup AIOCB. + this->aiocb_write_->aio_fildes = this->out_fd_; + this->aiocb_write_->aio_offset = 0; + this->aiocb_write_->aio_buf = this->buffer_write_; + this->aiocb_write_->aio_nbytes = strlen (this->buffer_write_); + this->aiocb_write_->aio_reqprio = 0; + this->aiocb_write_->aio_sigevent.sigev_notify = SIGEV_NONE; + //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX; + this->aiocb_write_->aio_sigevent.sigev_value.sival_ptr = + (void *) this->aiocb_write_; + + // Fire off the aio write. + if (aio_write (this->aiocb_write_) != 0) + { + perror ("aio_write"); + return -1; + } + + // = Read from that file. + + // Setup AIOCB. + this->aiocb_read_->aio_fildes = this->out_fd_; + this->aiocb_read_->aio_offset = 0; + this->aiocb_read_->aio_buf = this->buffer_read_; + this->aiocb_read_->aio_nbytes = strlen (this->buffer_write_); + this->aiocb_read_->aio_reqprio = 0; + this->aiocb_read_->aio_sigevent.sigev_notify = SIGEV_NONE; + //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX; + this->aiocb_read_->aio_sigevent.sigev_value.sival_ptr = + (void *) this->aiocb_read_; + + // Fire off the aio write. If it doesnt get queued, carry on to get + // the completion for the first one. + if (aio_read (this->aiocb_read_) < 0) + perror ("aio_read"); + + // Wait for the completion on aio_suspend. + struct aiocb *list_aiocb[2]; + list_aiocb [0] = this->aiocb_write_; + list_aiocb [1] = this->aiocb_read_; + + // Do suspend till all the aiocbs in the list are done. + int done = 0; + int return_val = 0; + while (!done) + { + return_val = aio_suspend (list_aiocb, + 2, + 0); + cerr << "Return value :" << return_val << endl; + + // Analyze return and error values. + if (list_aiocb[0] != 0) + { + if (aio_error (list_aiocb [0]) != EINPROGRESS) + { + if (aio_return (list_aiocb [0]) == -1) + { + perror ("aio_return"); + return -1; + } + else + { + // Successful. Store the pointer somewhere and make the + // entry NULL in the list. + this->aiocb_write_ = list_aiocb [0]; + list_aiocb [0] = 0; + } + } + else + cout << "AIO write in progress" << endl; + } + + if (list_aiocb[1] != 0) + { + if (aio_error (list_aiocb [1]) != EINPROGRESS) + { + int read_return = aio_return (list_aiocb[1]); + if (read_return == -1) + { + perror ("aio_return"); + return -1; + } + else + { + // Successful. Store the pointer somewhere and make the + // entry NULL in the list. + this->aiocb_read_ = list_aiocb [1]; + list_aiocb [1] = 0; + this->buffer_read_[read_return] = '\0'; + } + } + else + cout << "AIO read in progress" << endl; + } + + // Is it done? + if ((list_aiocb [0] == 0) && (list_aiocb [1] == 0)) + done = 1; + } + + cout << "Both the AIO operations done." << endl; + cout << "The buffer is :" << this->buffer_read_ << endl; + + return 0; +} + +int +main (int argc, char **argv) +{ + Test_Aio test_aio; + + if (test_aio.init () != 0) + { + printf ("AIOCB test failed:\n" + "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n"); + return -1; + } + + if (test_aio.do_aio () != 0) + { + printf ("AIOCB test failed:\n" + "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n"); + return -1; + } + printf ("AIOCB test successful:\n" + "ACE_POSIX_AIOCB_PROACTOR should work in this platform\n"); + return 0; +} diff --git a/ACE/examples/Reactor/Proactor/test_aiocb_ace.cpp b/ACE/examples/Reactor/Proactor/test_aiocb_ace.cpp new file mode 100644 index 00000000000..17705de1f03 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_aiocb_ace.cpp @@ -0,0 +1,259 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// proactor +// +// = FILENAME +// test_aiocb_ace.cpp +// +// = DESCRIPTION +// This program helps you to test the <aio_*> calls on a +// platform. +// +// Before running this test, make sure the platform can +// support POSIX <aio_> calls, using +// ACE_ROOT/tests/Aio_Platform_Test. +// +// This program tests the AIOCB (AIO Control Blocks) based +// completion approach which uses <aio_suspend> for completion +// querying. +// +// If this test is successful, ACE_POSIX_AIOCB_PROACTOR +// can be used on this platform. +// +// = COMPILE and RUN +// % make +// % ./test_aiocb_ace +// +// = AUTHOR +// Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/ACE.h" +#include "ace/Log_Msg.h" +#include "ace/os_include/os_aio.h" +#include "ace/OS_NS_string.h" + +class Test_Aio +{ +public: + Test_Aio (void); + // Default constructor. + + int init (void); + // Initting the output file and the buffer. + + int do_aio (void); + // Doing the testing stuff. + + ~Test_Aio (void); + // Destructor. +private: + int out_fd_; + // Output file descriptor. + + struct aiocb *aiocb_write_; + // For writing to the file. + + struct aiocb *aiocb_read_; + // Reading stuff from the file. + + char *buffer_write_; + // The buffer to be written to the out_fd. + + char *buffer_read_; + // The buffer to be read back from the file. +}; + +Test_Aio::Test_Aio (void) + : aiocb_write_ (0), + aiocb_read_ (0), + buffer_write_ (0), + buffer_read_ (0) +{ + ACE_NEW (this->aiocb_write_, + struct aiocb); + ACE_NEW (this->aiocb_read_, + struct aiocb); +} + +Test_Aio::~Test_Aio (void) +{ + delete aiocb_write_; + delete aiocb_read_; + delete buffer_write_; + delete buffer_read_; +} + +// Init the output file and init the buffer. +int +Test_Aio::init (void) +{ + // Open the output file. + this->out_fd_ = ACE_OS::open ("test_aio.log", + O_RDWR | O_CREAT | O_TRUNC, + 0666); + if (this->out_fd_ == 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Error: Opening file\n"), + -1); + + // Init the buffers. + this->buffer_write_ = ACE::strnew ("Welcome to the world of AIO... AIO Rules !!!"); + ACE_DEBUG ((LM_DEBUG, + "The buffer : %s\n", + this->buffer_write_)); + + // Allocate memory for the read buffer. + ACE_NEW_RETURN (this->buffer_read_, + char [ACE_OS::strlen (this->buffer_write_)], + -1); + + return 0; +} + +// Set the necessary things for the AIO stuff. +// Write the buffer asynchly.hmm Disable signals. +// Go on aio_suspend. Wait for completion. +// Print out the result. +int +Test_Aio::do_aio (void) +{ + // = Write to the file. + + // Setup AIOCB. + this->aiocb_write_->aio_fildes = this->out_fd_; + this->aiocb_write_->aio_offset = 0; + this->aiocb_write_->aio_buf = this->buffer_write_; + this->aiocb_write_->aio_nbytes = ACE_OS::strlen (this->buffer_write_); + this->aiocb_write_->aio_reqprio = 0; + this->aiocb_write_->aio_sigevent.sigev_notify = SIGEV_NONE; + //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX; + this->aiocb_write_->aio_sigevent.sigev_value.sival_ptr = + (void *) this->aiocb_write_; + + // Fire off the aio write. + if (aio_write (this->aiocb_write_) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "aio_write"), + -1); + + // = Read from that file. + + // Setup AIOCB. + this->aiocb_read_->aio_fildes = this->out_fd_; + this->aiocb_read_->aio_offset = 0; + this->aiocb_read_->aio_buf = this->buffer_read_; + this->aiocb_read_->aio_nbytes = ACE_OS::strlen (this->buffer_write_); + this->aiocb_read_->aio_reqprio = 0; + this->aiocb_read_->aio_sigevent.sigev_notify = SIGEV_NONE; + //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX; + this->aiocb_read_->aio_sigevent.sigev_value.sival_ptr = + (void *) this->aiocb_read_; + + // Fire off the aio write. If it doesnt get queued, carry on to get + // the completion for the first one. + if (aio_read (this->aiocb_read_) < 0) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "aio_read"), + -1); + + // Wait for the completion on aio_suspend. + struct aiocb *list_aiocb[2]; + list_aiocb [0] = this->aiocb_write_; + list_aiocb [1] = this->aiocb_read_; + + // Do suspend till all the aiocbs in the list are done. + int to_finish = 2; + int return_val = 0; + while (to_finish > 0) + { + return_val = aio_suspend (list_aiocb, + to_finish, + 0); + ACE_DEBUG ((LM_DEBUG, + "Result of <aio_suspend> : %d\n", + return_val)); + + // Analyze return and error values. + if (to_finish > 1) + { + if (aio_error (list_aiocb [1]) != EINPROGRESS) + { + if (aio_return (list_aiocb [1]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "aio_return, item 1"), + -1); + else + { + // Successful. Remember we have one less thing to finish. + --to_finish; + list_aiocb [1] = 0; + } + } + else + ACE_DEBUG ((LM_DEBUG, + "aio_error says aio 1 is in progress\n")); + } + + if (aio_error (list_aiocb [0]) != EINPROGRESS) + { + if (aio_return (list_aiocb [0]) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "aio_return, item 0"), + -1); + else + { + // Successful. Store the pointer somewhere and bump the + // read entry up to the front, if it is still not done. + --to_finish; + list_aiocb [0] = this->aiocb_read_; + } + } + else + ACE_DEBUG ((LM_DEBUG, + "aio_error says aio 0 is in progress\n")); + } + + ACE_DEBUG ((LM_DEBUG, + "Both the AIO operations done.\n" + "The buffer is : %s\n", + this->buffer_read_)); + + return 0; +} + +int +main (int argc, char **argv) +{ + + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + Test_Aio test_aio; + + if (test_aio.init () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "AIOCB test failed:\n" + "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n"), + -1); + + if (test_aio.do_aio () != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "AIOCB test failed:\n" + "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n"), + -1); + + ACE_DEBUG ((LM_DEBUG, + "AIOCB test successful:\n" + "ACE_POSIX_AIOCB_PROACTOR should work in this platform\n")); + + return 0; +} diff --git a/ACE/examples/Reactor/Proactor/test_aiosig.cpp b/ACE/examples/Reactor/Proactor/test_aiosig.cpp new file mode 100644 index 00000000000..1746a10a49c --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_aiosig.cpp @@ -0,0 +1,294 @@ +// $Id$ +// ============================================================================ +// +// = FILENAME +// test_aiosig.cpp +// +// = DESCRITPTION +// Check out test_aiosig_ace.cpp, the ACE'ified version of this +// program. This program may not be uptodate. +// +// = COMPILATION +// CC -g -o test_aiosig -lrt test_aiosig.cpp +// +// = RUN +// ./test_aiosig +// +// = AUTHOR +// Programming for the Real World. Bill O. GallMeister. +// Modified by Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + + +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +#include <limits.h> + +#include <aio.h> + +int file_handle = -1; +char mb1 [BUFSIZ + 1]; +char mb2 [BUFSIZ + 1]; +aiocb aiocb1, aiocb2; +sigset_t completion_signal; + +// Function prototypes. +int setup_signal_delivery (void); +int issue_aio_calls (void); +int query_aio_completions (void); +int test_aio_calls (void); +int setup_signal_handler (void); +int setup_signal_handler (int signal_number); + +int +setup_signal_delivery (void) +{ + // Make the sigset_t consisting of the completion signal. + if (sigemptyset (&completion_signal) == -1) + { + perror ("Error:Couldnt init the RT completion signal set\n"); + return -1; + } + + if (sigaddset (&completion_signal, SIGRTMIN) == -1) + { + perror ("Error:Couldnt init the RT completion signal set\n"); + return -1; + } + + // Mask them. + if (pthread_sigmask (SIG_BLOCK, &completion_signal, 0) == -1) + { + perror ("Error:Couldnt maks the RT completion signals\n"); + return -1; + } + + return setup_signal_handler (SIGRTMIN); +} + +int +issue_aio_calls (void) +{ + // Setup AIOCB. + aiocb1.aio_fildes = file_handle; + aiocb1.aio_offset = 0; + aiocb1.aio_buf = mb1; + aiocb1.aio_nbytes = BUFSIZ; + aiocb1.aio_reqprio = 0; + aiocb1.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocb1.aio_sigevent.sigev_signo = SIGRTMIN; + aiocb1.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb1; + + // Fire off the aio write. + if (aio_read (&aiocb1) == -1) + { + // Queueing failed. + perror ("Error:Asynch_Read_Stream: aio_read queueing failed\n"); + return -1; + } + + // Setup AIOCB. + aiocb2.aio_fildes = file_handle; + aiocb2.aio_offset = BUFSIZ + 1; + aiocb2.aio_buf = mb2; + aiocb2.aio_nbytes = BUFSIZ; + aiocb2.aio_reqprio = 0; + aiocb2.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocb2.aio_sigevent.sigev_signo = SIGRTMIN; + aiocb2.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb2; + + // Fire off the aio write. + if (aio_read (&aiocb2) == -1) + { + // Queueing failed. + perror ("Error:Asynch_Read_Stream: aio_read queueing failed\n"); + return -1; + } + return 0; +} + +int +query_aio_completions (void) +{ + int result = 0; + size_t number_of_compleions = 0; + for (number_of_compleions = 0; + number_of_compleions < 2; + number_of_compleions ++) + { + // Wait for <milli_seconds> amount of time. + // @@ Assigning <milli_seconds> to tv_sec. + timespec timeout; + timeout.tv_sec = INT_MAX; + timeout.tv_nsec = 0; + + // To get back the signal info. + siginfo_t sig_info; + + // Await the RT completion signal. + int sig_return = sigtimedwait (&completion_signal, + &sig_info, + &timeout); + + // Error case. + // If failure is coz of timeout, then return *0* but set + // errno appropriately. This is what the WinNT proactor + // does. + if (sig_return == -1) + { + perror ("Error:Error waiting for RT completion signals\n"); + return -1; + } + + // RT completion signals returned. + if (sig_return != SIGRTMIN) + { + printf ("Unexpected signal (%d) has been received while waiting for RT Completion Signals\n", + sig_return); + return -1; + } + + // @@ Debugging. + printf ("Sig number found in the sig_info block : %d\n", + sig_info.si_signo); + + // Is the signo returned consistent? + if (sig_info.si_signo != sig_return) + { + printf ("Inconsistent signal number (%d) in the signal info block\n", + sig_info.si_signo); + return -1; + } + + // @@ Debugging. + printf ("Signal code for this signal delivery : %d\n", + sig_info.si_code); + + // Is the signal code an aio completion one? + if ((sig_info.si_code != SI_ASYNCIO) && + (sig_info.si_code != SI_QUEUE)) + { + printf ("Unexpected signal code (%d) returned on completion querying\n", + sig_info.si_code); + return -1; + } + + // Retrive the aiocb. + aiocb* aiocb_ptr = (aiocb *) sig_info.si_value.sival_ptr; + + // Analyze error and return values. Return values are + // actually <errno>'s associated with the <aio_> call + // corresponding to aiocb_ptr. + int error_code = aio_error (aiocb_ptr); + if (error_code == -1) + { + perror ("Error:Invalid control block was sent to <aio_error> for compleion querying\n"); + return -1; + } + + if (error_code != 0) + { + // Error occurred in the <aio_>call. Return the errno + // corresponding to that <aio_> call. + printf ("Error:An AIO call has failed:Error code = %d\n", + error_code); + return -1; + } + + // No error occured in the AIO operation. + int nbytes = aio_return (aiocb_ptr); + if (nbytes == -1) + { + perror ("Error:Invalid control block was send to <aio_return>\n"); + return -1; + } + + if (number_of_compleions == 0) + // Print the buffer. + printf ("Number of bytes transferred : %d\n The buffer : %s \n", + nbytes, + mb1); + else + // Print the buffer. + printf ("Number of bytes transferred : %d\n The buffer : %s \n", + nbytes, + mb2); + } + return 0; +} + +int +test_aio_calls (void) +{ + // Set up the input file. + // Open file (in SEQUENTIAL_SCAN mode) + file_handle = open ("test_aiosig.cpp", O_RDONLY); + + if (file_handle == -1) + { + perror ("Error:Opening the inputfile"); + return -1; + } + + if (setup_signal_delivery () < 0) + return -1; + + if (issue_aio_calls () < 0) + return -1; + + if (query_aio_completions () < 0) + return -1; + + return 0; +} + +int +setup_signal_handler (int signal_number) +{ + // Setting up the handler(!) for these signals. + struct sigaction reaction; + sigemptyset (&reaction.sa_mask); // Nothing else to mask. + reaction.sa_flags = SA_SIGINFO; // Realtime flag. +#if defined (SA_SIGACTION) + // Lynx says, it is better to set this bit to be portable. + reaction.sa_flags &= SA_SIGACTION; +#endif /* SA_SIGACTION */ + reaction.sa_sigaction = null_handler; // Null handler. + int sigaction_return = sigaction (SIGRTMIN, + &reaction, + 0); + if (sigaction_return == -1) + { + perror ("Error:Proactor couldnt do sigaction for the RT SIGNAL"); + return -1; + } + + return 0; +} + +void +null_handler (int /* signal_number */, + siginfo_t * /* info */, + void * /* context */) +{ +} + +int +main (int, char *[]) +{ + if (test_aio_calls () == 0) + printf ("RT SIG test successful:\n" + "ACE_POSIX_SIG_PROACTOR should work in this platform\n"); + else + printf ("RT SIG test failed:\n" + "ACE_POSIX_SIG_PROACTOR may not work in this platform\n"); + return 0; +} diff --git a/ACE/examples/Reactor/Proactor/test_aiosig_ace.cpp b/ACE/examples/Reactor/Proactor/test_aiosig_ace.cpp new file mode 100644 index 00000000000..34c1b9b5ab2 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_aiosig_ace.cpp @@ -0,0 +1,358 @@ +// $Id$ + +// ============================================================================ +// +// = FILENAME +// test_aiosig_sig.cpp +// +// = DESCRITPTION +// This program helps you to test the <aio_*> calls on a +// platform. +// Before running this test, make sure the platform can +// support POSIX <aio_> calls, using ACE_ROOT/tests/Aio_Plaform_Test.cpp +// +// This program tests the Signal based completion approach which +// uses <sigtimedwait> for completion querying. +// If this test is successful, ACE_POSIX_SIG_PROACTOR +// can be used on this platform. +// +// This program is a ACE version of the +// $ACE_ROOT/examples/Reactor/Proactor/test_aiosig.cpp, with +// ACE_DEBUGs and Message_Blocks. +// +// This test does the following: +// Issue two <aio_read>s. +// Assign SIGRTMIN as the notification signal. +// Mask these signals from delivery. +// Receive this signal by doing <sigtimedwait>. +// Wait for two completions (two signals) +// +// = COMPILATION +// make +// +// = RUN +// ./test_aiosig_ace +// +// = AUTHOR +// Programming for the Real World. Bill O. GallMeister. +// Modified by Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + +#include "ace/Message_Block.h" +#include "ace/Log_Msg.h" +#include "ace/os_include/os_aio.h" +#include "ace/OS_NS_signal.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_fcntl.h" +#include "ace/Asynch_IO.h" // for ACE_INFINITE + +static ACE_HANDLE file_handle = ACE_INVALID_HANDLE; +static ACE_Message_Block mb1 (BUFSIZ + 1); +static ACE_Message_Block mb2 (BUFSIZ + 1); +static aiocb aiocb1; +static aiocb aiocb2; +static aiocb aiocb3; +static sigset_t completion_signal; + +// Function prototypes. +static int setup_signal_delivery (void); +static int issue_aio_calls (void); +static int query_aio_completions (void); +static int test_aio_calls (void); +static void null_handler (int signal_number, siginfo_t *info, void *context); +static int setup_signal_handler (int signal_number); + +static int +setup_signal_delivery (void) +{ + // = Mask all the signals. + + sigset_t full_set; + + // Get full set. + if (sigfillset (&full_set) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Error:(%P | %t):%p\n", + "sigfillset failed"), + -1); + + // Mask them. + if (ACE_OS::pthread_sigmask (SIG_SETMASK, &full_set, 0) != 0) + ACE_ERROR_RETURN ((LM_ERROR, + "Error:(%P | %t):%p\n", + "pthread_sigmask failed"), + -1); + + // = Make a mask with SIGRTMIN only. We use only that signal to + // issue <aio_>'s. + + if (sigemptyset (&completion_signal) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Couldnt init the RT completion signal set"), + -1); + + if (sigaddset (&completion_signal, + SIGRTMIN) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Couldnt init the RT completion signal set"), + -1); + + // Set up signal handler for this signal. + return setup_signal_handler (SIGRTMIN); +} + +static int +setup_signal_handler (int signal_number) +{ + ACE_UNUSED_ARG (signal_number); + + // Setting up the handler(!) for these signals. + struct sigaction reaction; + sigemptyset (&reaction.sa_mask); // Nothing else to mask. + reaction.sa_flags = SA_SIGINFO; // Realtime flag. +#if defined (SA_SIGACTION) + // Lynx says, it is better to set this bit to be portable. + reaction.sa_flags &= SA_SIGACTION; +#endif /* SA_SIGACTION */ + reaction.sa_sigaction = null_handler; // Null handler. + int sigaction_return = sigaction (SIGRTMIN, + &reaction, + 0); + if (sigaction_return == -1) + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Proactor couldnt do sigaction for the RT SIGNAL"), + -1); + return 0; +} + + +static int +issue_aio_calls (void) +{ + // Setup AIOCB. + aiocb1.aio_fildes = file_handle; + aiocb1.aio_offset = 0; + aiocb1.aio_buf = mb1.wr_ptr (); + aiocb1.aio_nbytes = BUFSIZ; + aiocb1.aio_reqprio = 0; + aiocb1.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocb1.aio_sigevent.sigev_signo = SIGRTMIN; + aiocb1.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb1; + + // Fire off the aio read. + if (aio_read (&aiocb1) == -1) + // Queueing failed. + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Asynch_Read_Stream: aio_read queueing failed"), + -1); + + // Setup AIOCB. + aiocb2.aio_fildes = file_handle; + aiocb2.aio_offset = BUFSIZ + 1; + aiocb2.aio_buf = mb2.wr_ptr (); + aiocb2.aio_nbytes = BUFSIZ; + aiocb2.aio_reqprio = 0; + aiocb2.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocb2.aio_sigevent.sigev_signo = SIGRTMIN; + aiocb2.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb2; + + // Fire off the aio read. + if (aio_read (&aiocb2) == -1) + // Queueing failed. + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Asynch_Read_Stream: aio_read queueing failed"), + -1); + + // Setup sigval. + aiocb3.aio_fildes = ACE_INVALID_HANDLE; + aiocb3.aio_offset = 0; + aiocb3.aio_buf = 0; + aiocb3.aio_nbytes = 0; + aiocb3.aio_reqprio = 0; + aiocb3.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocb3.aio_sigevent.sigev_signo = SIGRTMIN; + aiocb3.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb3; + sigval value; + value.sival_ptr = reinterpret_cast<void *> (&aiocb3); + // Queue this one for completion right now. + if (sigqueue (ACE_OS::getpid (), SIGRTMIN, value) == -1) + // Queueing failed. + ACE_ERROR_RETURN ((LM_ERROR, + "Error: %p\n", "sigqueue"), + -1); + + return 0; +} + +static int +query_aio_completions (void) +{ + for (size_t number_of_compleions = 0; + number_of_compleions < 3; + number_of_compleions ++) + { + // Wait for <milli_seconds> amount of time. @@ Assigning + // <milli_seconds> to tv_sec. + timespec timeout; + timeout.tv_sec = ACE_INFINITE; + timeout.tv_nsec = 0; + + // To get back the signal info. + siginfo_t sig_info; + + // Await the RT completion signal. + int sig_return = sigtimedwait (&completion_signal, + &sig_info, + &timeout); + + // Error case. + // If failure is coz of timeout, then return *0* but set + // errno appropriately. This is what the WinNT proactor + // does. + if (sig_return == -1) + ACE_ERROR_RETURN ((LM_ERROR, "Error: %p\n", + "Error waiting for RT completion signals"), + -1); + + // RT completion signals returned. + if (sig_return != SIGRTMIN) + ACE_ERROR_RETURN ((LM_ERROR, + "Unexpected signal (%d) has been received while waiting for RT Completion Signals\n", + sig_return), + -1); + + // @@ Debugging. + ACE_DEBUG ((LM_DEBUG, + "Sig number found in the sig_info block : %d\n", + sig_info.si_signo)); + + // Is the signo returned consistent? + if (sig_info.si_signo != sig_return) + ACE_ERROR_RETURN ((LM_ERROR, + "Inconsistent signal number (%d) in the signal info block\n", + sig_info.si_signo), + -1); + + // @@ Debugging. + ACE_DEBUG ((LM_DEBUG, + "Signal code for this signal delivery : %d\n", + sig_info.si_code)); + + // Is the signal code an aio completion one? + if ((sig_info.si_code != SI_ASYNCIO) && + (sig_info.si_code != SI_QUEUE)) + ACE_ERROR_RETURN ((LM_DEBUG, + "Unexpected signal code (%d) returned on completion querying\n", + sig_info.si_code), + -1); + + // Retrive the aiocb. + aiocb* aiocb_ptr = (aiocb *) sig_info.si_value.sival_ptr; + if (aiocb_ptr == &aiocb3) + { + ACE_ASSERT (sig_info.si_code == SI_QUEUE); + ACE_DEBUG ((LM_DEBUG, "sigqueue caught... good\n")); + } + else + { + // Analyze error and return values. Return values are + // actually <errno>'s associated with the <aio_> call + // corresponding to aiocb_ptr. + int error_code = aio_error (aiocb_ptr); + if (error_code == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", + "Invalid control block was sent to <aio_error> for completion querying"), + -1); + + if (error_code != 0) + // Error occurred in the <aio_>call. Return the errno + // corresponding to that <aio_> call. + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", + "An AIO call has failed"), + error_code); + + // No error occured in the AIO operation. + int nbytes = aio_return (aiocb_ptr); + if (nbytes == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", + "Invalid control block was send to <aio_return>"), + -1); + if (number_of_compleions == 0) + { + // Print the buffer. + ACE_DEBUG ((LM_DEBUG, + "\n Number of bytes transferred : %d\n", + nbytes)); + // Note... the dumps of the buffers are disabled because they + // may easily overrun the ACE_Log_Msg output buffer. If you need + // to turn the on for some reason, be careful of this. +#if 0 + ACE_DEBUG ((LM_DEBUG, "The buffer : %s \n", mb1.rd_ptr ())); +#endif /* 0 */ + } + else + { + // Print the buffer. + ACE_DEBUG ((LM_DEBUG, + "\n Number of bytes transferred : %d\n", + nbytes)); +#if 0 + ACE_DEBUG ((LM_DEBUG, "The buffer : %s \n", mb2.rd_ptr ())); +#endif /* 0 */ + } + } + } + + return 0; +} + +static int +test_aio_calls (void) +{ + // Set up the input file. + // Open file (in SEQUENTIAL_SCAN mode) + file_handle = ACE_OS::open ("test_aiosig_ace.cpp", + O_RDONLY); + + if (file_handle == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_OS::open"), + -1); + + if (setup_signal_delivery () == -1) + return -1; + + if (issue_aio_calls () == -1) + return -1; + + if (query_aio_completions () == -1) + return -1; + + return 0; +} + +static void +null_handler (int signal_number, + siginfo_t */* info */, + void * /* context */) +{ + ACE_ERROR ((LM_ERROR, + "Error:%s:Signal number %d\n" + "Mask all the RT signals for this thread", + "ACE_POSIX_SIG_Proactor::null_handler called", + signal_number)); +} + +int +main (int, char *[]) +{ + if (test_aio_calls () == 0) + printf ("RT SIG test successful:\n" + "ACE_POSIX_SIG_PROACTOR should work in this platform\n"); + else + printf ("RT SIG test failed:\n" + "ACE_POSIX_SIG_PROACTOR may not work in this platform\n"); + return 0; +} diff --git a/ACE/examples/Reactor/Proactor/test_cancel.cpp b/ACE/examples/Reactor/Proactor/test_cancel.cpp new file mode 100644 index 00000000000..c10f8e9be2c --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_cancel.cpp @@ -0,0 +1,246 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_cancel.cpp +// +// = DESCRIPTION +// This program tests cancelling an Asynchronous Operation in the +// Proactor framework. +// +// This tests accepts a connection and issues an Asynchronous Read +// Stream. It reads <read_size> (option -s) number of bytes and +// when this operation completes, it issues another Asynchronous +// Read Stream to <read_size> and immediately calls <cancel> to +// cancel the operation and so the program exits closing the +// connection. +// +// Works fine on NT. On Solaris platforms, the asynch read is +// pending, but the cancel returns with the value <AIO_ALLDONE> +// indicating all the operations in that handle are done. +// But, LynxOS has a good <aio_cancel> implementation. It works +// fine. +// +// = RUN +// ./test_cancel -p <port_number> +// Then telnet to this port and send <read_size> bytes and your +// connection should get closed down. +// +// = AUTHOR +// Irfan Pyarali (irfan@cs.wustl.edu) +// +// ============================================================================ + +#include "ace/OS_main.h" +#include "ace/Service_Config.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/Asynch_IO_Impl.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_sys_socket.h" + +ACE_RCSID (Proactor, test_proactor, "$Id$") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms supporting + // POSIX aio calls. + +#include "test_cancel.h" + +static u_short port = ACE_DEFAULT_SERVER_PORT; +static int done = 0; +static int read_size = 2; + + +Receiver::Receiver (void) + : mb_ (read_size + 1), + handle_ (ACE_INVALID_HANDLE) +{ +} + +Receiver::~Receiver (void) +{ + ACE_DEBUG ((LM_DEBUG, + "Receiver: Closing down Remote connection:%d\n", + this->handle_)); + + ACE_OS::closesocket (this->handle_); +} + +void +Receiver::open (ACE_HANDLE handle, + ACE_Message_Block &) +{ + // New connection, initiate stuff + + ACE_DEBUG ((LM_DEBUG, "%N:%l:Receiver::open called\n")); + + // Cache the new connection + this->handle_ = handle; + + // Initiate ACE_Asynch_Read_Stream + if (this->rs_.open (*this, this->handle_) == -1) + { + ACE_ERROR ((LM_ERROR, "%p\n", "ACE_Asynch_Read_Stream::open")); + return; + } + + // Try to read <n> bytes from the stream. + + ACE_DEBUG ((LM_DEBUG, + "Receiver::open: Issuing Asynch Read of (%d) bytes from the stream\n", + read_size)); + + if (this->rs_.read (this->mb_, + read_size) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "Receiver::open: Failed to issue the read")); +} + +void +Receiver::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, "handle_read_stream called\n")); + + // Reset pointers + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); + + if (result.success () && !result.error ()) + { + // Successful read: No error. + + // Set the pointers back in the message block. + result.message_block ().wr_ptr (result.message_block ().rd_ptr ()); + + // Issue another read, but immediately cancel it. + + // Issue the read. + + ACE_DEBUG ((LM_DEBUG, + "Issuing Asynch Read of (%d) bytes from the stream\n", + read_size)); + + if (this->rs_.read (this->mb_, + read_size) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "Receiver::handle_read_stream: Failed to issue the read")); + + // Cancel the read. + + ACE_DEBUG ((LM_DEBUG, + "Cacelling Asynch Read ")); + + int ret_val = this->rs_.cancel (); + if (ret_val == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "Receiver::handle_read_stream: Failed to cancel the read")); + + ACE_DEBUG ((LM_DEBUG, "Asynch IO : Cancel : Result = %d\n", + ret_val)); + } + else + { + done = 1; + + ACE_DEBUG ((LM_DEBUG, "Receiver completed\n")); + + // Print the error message if any. + if (result.error () != 0) + { + errno = result.error (); + + ACE_ERROR ((LM_ERROR, + "%p\n", + "Asynch Read Stream Error: ")); + } + } +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("p:s:")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'p': + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': + read_size = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, "%p.\n", + "usage :\n" + "-p <port>\n" + "-s <read_size>\n")); + return -1; + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + // Note: acceptor parameterized by the Receiver + ACE_Asynch_Acceptor<Receiver> acceptor; + + // Listening passively. + if (acceptor.open (ACE_INET_Addr (port), + read_size, + 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "ACE:acceptor::open failed\n"), + 1); + + int success = 1; + + while (success > 0 && !done) + // dispatch events + success = ACE_Proactor::instance ()->handle_events (); + + return 0; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example does not work on this platform.\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_cancel.h b/ACE/examples/Reactor/Proactor/test_cancel.h new file mode 100644 index 00000000000..45c4bfbc85b --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_cancel.h @@ -0,0 +1,47 @@ +/* +** $Id$ +*/ + +#ifndef _TEST_CANCEL_H +#define _TEST_CANCEL_H + +#include "ace/Asynch_IO.h" + +class Receiver : public ACE_Service_Handler +{ + // = TITLE + // + // Receiver + // + // = DESCRIPTION + // + // The class will be created by ACE_Asynch_Acceptor when new + // connections arrive. This class will then receive data from + // the network connection and dump it to a file. + +public: + Receiver (void); + ~Receiver (void); + + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + // This is called after the new connection has been accepted. + +protected: + // These methods are called by the framework + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This is called when asynchronous read from the socket complete + +private: + ACE_Asynch_Read_Stream rs_; + // rs (read stream): for reading from a socket + + ACE_Message_Block mb_; + // Message block to read from the stream. + + ACE_HANDLE handle_; + // Handle for IO to remote peer +}; + +#endif /* _TEST_CANCEL_H */ diff --git a/ACE/examples/Reactor/Proactor/test_end_event_loop.cpp b/ACE/examples/Reactor/Proactor/test_end_event_loop.cpp new file mode 100644 index 00000000000..096f77b089d --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_end_event_loop.cpp @@ -0,0 +1,168 @@ +// $Id$ +// ============================================================================ +// +// = FILENAME +// test_end_event_loop.cpp +// +// = DESCRITPTION +// This program tests the event loop mechanism of the +// Proactor. To end the event loop, threads that are blocked in +// waiting for completions are woken up and the event loop comes +// to the end. This is tested in this program. +// +// Threads are doing <run_event_loop> with/without time_out +// values and the main thread calls <end_event_loop>. +// +// = COMPILATION +// make +// +// = RUN +// ./test_end_event_loop +// +// = AUTHOR +// Alexander Babu Arulanthu <alex@cs.wustl.edu> +// +// ===================================================================== + +#include "ace/OS_NS_unistd.h" +#include "ace/Proactor.h" +#include "ace/Task.h" +#include "ace/WIN32_Proactor.h" +#include "ace/POSIX_Proactor.h" +#include "ace/OS_main.h" + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \ + (defined (ACE_HAS_AIO_CALLS)) && !defined (ACE_POSIX_AIOCB_PROACTOR)) +// This only works on Win32 platforms and on Unix platforms supporting +// POSIX aio calls. + +class My_Task: public ACE_Task <ACE_NULL_SYNCH> +{ + // = TITLE + // + // Contains thread functions which execute event loops. Each + // thread waits for a different signal. + // +public: + // Constructor. + My_Task (void) + : time_flag_ (0) + {} + + + virtual ~My_Task (void) {} + // Destructor. + + // If time_flag is zero do the eventloop indefinitely, otherwise do + // it for finite amount of time (13secs!!!). + int open (void *timed_event_loop) + { + // Set the local variable. + if (timed_event_loop == 0) + this->time_flag_ = 0; + else + this->time_flag_ = 1; + + // Spawn the threads. + if (this->activate (THR_NEW_LWP, 5) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%N:%l:%p\n", + "My_Task:open: <activate> failed"), + -1); + + return 0; + } + + // Thread function. + int svc (void) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t):Starting svc routine\n")); + + if (this->time_flag_) + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t):Going to do *timed* <run_event_loop> \n")); + + ACE_Time_Value run_time (13); + + if (ACE_Proactor::instance ()->run_event_loop (run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t):%p.\n", + "<Proactor::run_event_loop> failed"), + -1); + } + else + { + ACE_DEBUG ((LM_DEBUG, + "(%P|%t):Going to do *indefinite* <run_event_loop> \n")); + + if (ACE_Proactor::instance ()->run_event_loop () == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t):%p.\n", + "<Proactor::run_event_loop> failed"), + -1); + } + return 0; + }; + +private: + int time_flag_; + // If zero, indefinite event loop, otherwise timed event loop. +}; + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv []) +{ + ACE_UNUSED_ARG (argc); + ACE_UNUSED_ARG (argv); + + ACE_DEBUG ((LM_DEBUG, + "(%P | %t):Test starts \n")); + + // Let us get the singleton proactor created here. This is very + // important. This will mask the signal used in the Proactor masked + // for the main thread (and all the threads). + ACE_Proactor *proactor = ACE_Proactor::instance (); + ACE_UNUSED_ARG (proactor); + + My_Task task1, task2; + + // Test the indefinite run event loop. + if (task1.open (0) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%N:%l:(%P | %t):Failed to <open> the task\n"), + 1); + + // Test the indefinite run event loop. Just pass a non-zero. + if (task2.open ((void *)&task2) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%N:%l:(%P | %t):Failed to <open> the task\n"), + 1); + + // Give a gap. + ACE_OS::sleep (3); + + // End the event loop. + if (ACE_Proactor::instance ()->end_event_loop () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%N:%l:(%P | %t):Failed to <end_event_loop>\n"), + 1); + + ACE_Thread_Manager::instance ()->wait (); + + ACE_DEBUG ((LM_DEBUG, + "(%P | %t):Test ends\n")); + return 0; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ + +int +main (int, char *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example cannot work with AIOCB_Proactor.\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ + diff --git a/ACE/examples/Reactor/Proactor/test_multiple_loops.cpp b/ACE/examples/Reactor/Proactor/test_multiple_loops.cpp new file mode 100644 index 00000000000..ac4228ab641 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_multiple_loops.cpp @@ -0,0 +1,140 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_multiple_loops.cpp +// +// = DESCRIPTION +// +// This example application shows how to write programs that +// combine the Proactor and Reactor event loops. This is possible +// only on WIN32 platform. +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/Task.h" +#include "ace/Proactor.h" +#include "ace/WIN32_Proactor.h" +#include "ace/Atomic_Op.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(Proactor, test_multiple_loops, "$Id$") + +#if (defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) + +class Timeout_Handler : public ACE_Handler, public ACE_Event_Handler +{ + // = TITLE + // Generic timeout handler. + +public: + Timeout_Handler (void) + { + } + + // This is called by the Proactor. This is declared in ACE_Handler. + virtual void handle_time_out (const ACE_Time_Value &tv, + const void *arg) + { + // Print out when timeouts occur. + ACE_DEBUG ((LM_DEBUG, "(%t) %d timeout occurred for %s @ %d.\n", + ++count_, + (char *) arg, + tv.sec ())); + + // Since there is only one thread that can do the timeouts in + // Reactor, lets keep the handle_timeout short for that + // thread. + if (ACE_OS::strcmp ((char *) arg, "Proactor") == 0) + // Sleep for a while + ACE_OS::sleep (1); + } + + // This method is declared in ACE_Event_Handler. + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg) + { + this->handle_time_out (tv, arg); + return 0; + } + +private: + ACE_Atomic_Op <ACE_Thread_Mutex, int> count_; +}; + +class Worker : public ACE_Task <ACE_NULL_SYNCH> +{ +public: + + // Thread fuction. + int svc (void) + { + ACE_DEBUG ((LM_DEBUG, "(%t) Worker started\n")); + + // Handle events for 13 seconds. + ACE_Time_Value run_time (13); + + // Try to become the owner + ACE_Reactor::instance ()->owner (ACE_Thread::self ()); + + if (ACE_Reactor::run_event_loop (run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "Worker::svc"), -1); + else + ACE_DEBUG ((LM_DEBUG, "(%t) work complete\n")); + + return 0; + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Timeout_Handler handler; + ACE_WIN32_Proactor win32_proactor (0, 1); + ACE_Proactor proactor (&win32_proactor, 0, 0); + + ACE_Reactor::instance ()->register_handler (proactor.implementation ()); + + // Register a 2 second timer. + ACE_Time_Value foo_tv (2); + if (proactor.schedule_timer (handler, + (void *) "Proactor", + ACE_Time_Value::zero, + foo_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + // Register a 3 second timer. + ACE_Time_Value bar_tv (3); + if (ACE_Reactor::instance ()->schedule_timer (&handler, + (void *) "Reactor", + ACE_Time_Value::zero, + bar_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + Worker worker; + + if (worker.activate (THR_NEW_LWP, 10) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); + + ACE_Thread_Manager::instance ()->wait (); + + // Remove from reactor + ACE_Reactor::instance ()->remove_handler (&proactor, + ACE_Event_Handler::DONT_CALL); + + return 0; +} +#else +int +main (int, char *[]) +{ + return 0; +} +#endif /* ACE_WIN32 && !ACE_HAS_WINCE */ diff --git a/ACE/examples/Reactor/Proactor/test_proactor.cpp b/ACE/examples/Reactor/Proactor/test_proactor.cpp new file mode 100644 index 00000000000..035a2facf6a --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_proactor.cpp @@ -0,0 +1,679 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_proactor.cpp +// +// = DESCRIPTION +// This program illustrates how the <ACE_Proactor> can be used to +// implement an application that does various asynchronous +// operations. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/OS_NS_string.h" +#include "ace/OS_main.h" +#include "ace/Service_Config.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/Asynch_IO_Impl.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" +#include "ace/Log_Msg.h" +#include "ace/OS_NS_sys_stat.h" +#include "ace/OS_NS_sys_socket.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_fcntl.h" + +ACE_RCSID(Proactor, test_proactor, "$Id$") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms supporting + // POSIX aio calls. + +#include "test_proactor.h" + + +// Host that we're connecting to. +static ACE_TCHAR *host = 0; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// File that we're sending. +static const ACE_TCHAR *file = ACE_TEXT("test_proactor.cpp"); + +// Name of the output file. +static const ACE_TCHAR *dump_file = ACE_TEXT("output"); + +// Keep track of when we're done. +static int done = 0; + +// Size of each initial asynchronous <read> operation. +static int initial_read_size = BUFSIZ; + + +Receiver::Receiver (void) + : dump_file_ (ACE_INVALID_HANDLE), + handle_ (ACE_INVALID_HANDLE) +{ +} + +Receiver::~Receiver (void) +{ + ACE_OS::close (this->dump_file_); + ACE_OS::closesocket (this->handle_); +} + +void +Receiver::open (ACE_HANDLE handle, + ACE_Message_Block &message_block) +{ + ACE_DEBUG ((LM_DEBUG, + "%N:%l:Receiver::open called\n")); + + // New connection, so initiate stuff. + + // Cache the new connection + this->handle_ = handle; + + // File offset starts at zero + this->file_offset_ = 0; + + // Open dump file (in OVERLAPPED mode) + this->dump_file_ = ACE_OS::open (dump_file, + O_CREAT | O_RDWR | O_TRUNC | \ + FILE_FLAG_OVERLAPPED); + if (this->dump_file_ == ACE_INVALID_HANDLE) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_OS::open")); + return; + } + + // Initiate <ACE_Asynch_Write_File>. + if (this->wf_.open (*this, + this->dump_file_) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_File::open")); + return; + } + + // Initiate <ACE_Asynch_Read_Stream>. + if (this->rs_.open (*this, this->handle_) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::open")); + return; + } + + // Fake the result and make the <handle_read_stream> get + // called. But, not, if there is '0' is transferred. + if (message_block.length () != 0) + { + // Duplicate the message block so that we can keep it around. + ACE_Message_Block &duplicate = + *message_block.duplicate (); + + // Fake the result so that we will get called back. + ACE_Asynch_Read_Stream_Result_Impl *fake_result = + ACE_Proactor::instance ()->create_asynch_read_stream_result (this->proxy (), + this->handle_, + duplicate, + initial_read_size, + 0, + ACE_INVALID_HANDLE, + 0, + 0); + + size_t bytes_transferred = message_block.length (); + + // <complete> for Accept would have already moved the <wr_ptr> + // forward. Update it to the beginning position. + duplicate.wr_ptr (duplicate.wr_ptr () - bytes_transferred); + + // This will call the callback. + fake_result->complete (message_block.length (), + 1, + 0); + + // Zap the fake result. + delete fake_result; + } + else + // Otherwise, make sure we proceed. Initiate reading the socket + // stream. + if (this->initiate_read_stream () == -1) + return; +} + +int +Receiver::initiate_read_stream (void) +{ + // Create a new <Message_Block>. Note that this message block will + // be used both to <read> data asynchronously from the socket and to + // <write> data asynchronously to the file. + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, + mb->size () - 1) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::read"), + -1); + return 0; +} + +void +Receiver::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_read_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); +#if 0 + // This can overrun the ACE_Log_Msg buffer and do bad things. + // Re-enable it at your risk. + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); +#endif /* 0 */ + + if (result.success () && result.bytes_transferred () != 0) + { + // Successful read: write the data to the file asynchronously. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + if (this->wf_.write (result.message_block (), + result.bytes_transferred (), + this->file_offset_) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_File::write")); + return; + } + + // Initiate new read from the stream. + if (this->initiate_read_stream () == -1) + return; + } + else + { + ACE_DEBUG ((LM_DEBUG, + "Receiver completed\n")); + + // No need for this message block anymore. + result.message_block ().release (); + + // Note that we are done with the test. + done = 1; + + // We are done: commit suicide. + delete this; + } +} + +void +Receiver::handle_write_file (const ACE_Asynch_Write_File::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, "handle_write_file called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + + result.message_block ().release (); + + if (result.success ()) + // Write successful: Increment file offset + this->file_offset_ += result.bytes_transferred (); + + // This code is not robust enough to deal with short file writes + // (which hardly ever happen) ;-) + ACE_ASSERT (result.bytes_to_write () == result.bytes_transferred ()); +} + +class Sender : public ACE_Handler +{ + // = TITLE + // The class will be created by <main>. After connecting to the + // host, this class will then read data from a file and send it + // to the network connection. +public: + Sender (void); + ~Sender (void); + int open (const ACE_TCHAR *host, + u_short port); + ACE_HANDLE handle (void) const; + void handle (ACE_HANDLE); + +protected: + // These methods are called by the freamwork + + virtual void handle_transmit_file (const ACE_Asynch_Transmit_File::Result &result); + // This is called when asynchronous transmit files complete + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This is called when asynchronous writes from the socket complete + virtual void handle_read_file (const ACE_Asynch_Read_File::Result &result); + // This is called when asynchronous reads from the socket complete + +private: + int transmit_file (void); + // Transmit the entire file in one fell swoop. + + int initiate_read_file (void); + // Initiate an asynchronous file read. + + ACE_SOCK_Stream stream_; + // Network I/O handle + + ACE_Asynch_Write_Stream ws_; + // ws (write stream): for writing to the socket + + ACE_Asynch_Read_File rf_; + // rf (read file): for writing from the file + + ACE_Asynch_Transmit_File tf_; + // Transmit file. + + ACE_HANDLE input_file_; + // File to read from + + u_long file_offset_; + // Current file offset + + u_long file_size_; + // File size + + ACE_Message_Block welcome_message_; + // Welcome message + + ACE_Asynch_Transmit_File::Header_And_Trailer header_and_trailer_; + // Header and trailer which goes with transmit_file + + int stream_write_done_; + int transmit_file_done_; + // These flags help to determine when to close down the event loop +}; + +Sender::Sender (void) + : input_file_ (ACE_INVALID_HANDLE), + file_offset_ (0), + file_size_ (0), + stream_write_done_ (0), + transmit_file_done_ (0) +{ + // Moment of inspiration... :-) + static const char *data = "Welcome to Irfan World! Irfan RULES here !!\n"; + this->welcome_message_.init (data, + ACE_OS::strlen (data)); + this->welcome_message_.wr_ptr (ACE_OS::strlen (data)); +} + +Sender::~Sender (void) +{ + this->stream_.close (); +} + +ACE_HANDLE +Sender::handle (void) const +{ + return this->stream_.get_handle (); +} + +void +Sender::handle (ACE_HANDLE handle) +{ + this->stream_.set_handle (handle); +} + +int +Sender::open (const ACE_TCHAR *host, + u_short port) +{ + // Initialize stuff + + // Open input file (in OVERLAPPED mode) + this->input_file_ = + ACE_OS::open (file, GENERIC_READ | FILE_FLAG_OVERLAPPED); + if (this->input_file_ == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_OS::open"), -1); + + // Find file size + this->file_size_ = + ACE_OS::filesize (this->input_file_); + + // Connect to remote host + ACE_INET_Addr address (port, host); + ACE_SOCK_Connector connector; + if (connector.connect (this->stream_, + address) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_SOCK_Connector::connect"), + -1); + + // Open ACE_Asynch_Write_Stream + if (this->ws_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::open"), + -1); + + // Open ACE_Asynch_Read_File + if (this->rf_.open (*this, this->input_file_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_File::open"), + -1); + + // Start an asynchronous transmit file + if (this->transmit_file () == -1) + return -1; + + // Start an asynchronous read file + if (this->initiate_read_file () == -1) + return -1; + + return 0; +} + +int +Sender::transmit_file (void) +{ + // Open file (in SEQUENTIAL_SCAN mode) + ACE_HANDLE file_handle = + ACE_OS::open (file, GENERIC_READ | FILE_FLAG_SEQUENTIAL_SCAN); + if (file_handle == ACE_INVALID_HANDLE) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_OS::open"), + -1); + + // Open ACE_Asynch_Transmit_File + if (this->tf_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Transmit_File::open"), + -1); + + // Header and trailer data for the file. + // @@ What happens if header and trailer are the same? + this->header_and_trailer_.header_and_trailer (&this->welcome_message_, + this->welcome_message_.length (), + &this->welcome_message_, + this->welcome_message_.length ()); + + // Send the entire file in one fell swoop! + if (this->tf_.transmit_file (file_handle, + &this->header_and_trailer_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Transmit_File::transmit_file"), + -1); + + return 0; +} + +void +Sender::handle_transmit_file (const ACE_Asynch_Transmit_File::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_transmit_file called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "socket", result.socket ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "file", result.file ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_per_send", result.bytes_per_send ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "flags", result.flags ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + + // Done with file + ACE_OS::close (result.file ()); + + this->transmit_file_done_ = 1; + if (this->stream_write_done_) + done = 1; +} + +int +Sender::initiate_read_file (void) +{ + // Create a new <Message_Block>. Note that this message block will + // be used both to <read> data asynchronously from the file and to + // <write> data asynchronously to the socket. + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate an asynchronous read from the file + if (this->rf_.read (*mb, + mb->size () - 1, + this->file_offset_) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_File::read"), + -1); + return 0; +} + +void +Sender::handle_read_file (const ACE_Asynch_Read_File::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_read_file called\n")); + + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + //ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); + + if (result.success ()) + { + // Read successful: increment offset and write data to network. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + + this->file_offset_ += result.bytes_transferred (); + if (this->ws_.write (result.message_block (), + result.bytes_transferred ()) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::write")); + return; + } + + if (this->file_size_ > this->file_offset_) + { + // Start an asynchronous read file. + if (initiate_read_file () == -1) + return; + } + } +} + +void +Sender::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_write_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr (result.message_block ().rd_ptr () - result.bytes_transferred ()); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); +#if 0 + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); +#endif + + if (result.success ()) + { + // Partial write to socket + int unsent_data = result.bytes_to_write () - result.bytes_transferred (); + if (unsent_data != 0) + { + // Reset pointers + result.message_block ().rd_ptr (result.bytes_transferred ()); + + // Duplicate the message block and retry remaining data + if (this->ws_.write (*result.message_block ().duplicate (), + unsent_data) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::write")); + return; + } + } + else if (!(this->file_size_ > this->file_offset_)) + { + this->stream_write_done_ = 1; + if (this->transmit_file_done_) + done = 1; + } + } + + // Release message block. + result.message_block ().release (); +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("h:p:f:d:")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'h': + host = get_opt.opt_arg (); + break; + case 'p': + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'f': + file = get_opt.opt_arg (); + break; + case 'd': + dump_file = get_opt.opt_arg (); + break; + default: + ACE_ERROR ((LM_ERROR, "%p.\n", + "usage :\n" + "-h <host>\n" + "-p <port>\n" + "-f <file>\n")); + return -1; + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + Sender sender; + + // Note: acceptor parameterized by the Receiver. + ACE_Asynch_Acceptor<Receiver> acceptor; + + // If passive side + if (host == 0) + { + if (acceptor.open (ACE_INET_Addr (port), + initial_read_size, + 1) == -1) + return -1; + } + // If active side + else if (sender.open (host, port) == -1) + return -1; + + int success = 1; + + while (success > 0 && !done) + // Dispatch events via Proactor singleton. + success = ACE_Proactor::instance ()->handle_events (); + + return 0; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example does not work on this platform.\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_proactor.h b/ACE/examples/Reactor/Proactor/test_proactor.h new file mode 100644 index 00000000000..482e176041e --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_proactor.h @@ -0,0 +1,56 @@ +/* +** $Id$ +*/ + +#ifndef _TEST_PROACTOR_H +#define _TEST_PROACTOR_H + +#include "ace/Asynch_IO.h" + +class Receiver : public ACE_Service_Handler +{ + // = TITLE + // The class will be created by <ACE_Asynch_Acceptor> when new + // connections arrive. This class will then receive data from + // the network connection and dump it to a file. +public: + // = Initialization and termination. + Receiver (void); + ~Receiver (void); + + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + // This is called after the new connection has been accepted. + +protected: + // These methods are called by the framework + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This is called when asynchronous <read> operation from the socket + // complete. + + virtual void handle_write_file (const ACE_Asynch_Write_File::Result &result); + // This is called when an asynchronous <write> to the file + // completes. + +private: + int initiate_read_stream (void); + // Initiate an asynchronous <read> operation on the socket. + + ACE_Asynch_Read_Stream rs_; + // rs (read stream): for reading from a socket. + + ACE_HANDLE dump_file_; + // File for dumping data. + + ACE_Asynch_Write_File wf_; + // wf (write file): for writing to a file. + + u_long file_offset_; + // Offset for the file. + + ACE_HANDLE handle_; + // Handle for IO to remote peer. +}; + +#endif /* _TEST_PROACTOR_H */ diff --git a/ACE/examples/Reactor/Proactor/test_proactor2.cpp b/ACE/examples/Reactor/Proactor/test_proactor2.cpp new file mode 100644 index 00000000000..cd5cbf7092e --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_proactor2.cpp @@ -0,0 +1,808 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_proactor2.cpp +// +// = DESCRIPTION +// Alexander Libman <Alibman@baltimore.com> modified +// <test_proactor> and made this test. Instead of writing received +// data to the file, the receiver sends them back to the +// sender,i.e. ACE_Asynch_Write_File wf_ has been changed to +// ACE_Asynch_Write_Stream wf_. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and Alexander Libman +// <Alibman@baltimore.com>. +// ============================================================================ + +#include "ace/Signal.h" + +#include "ace/Service_Config.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/Asynch_IO_Impl.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/Task.h" +#include "ace/OS_main.h" + +ACE_RCSID(Proactor, test_proactor2, "test_proactor2.cpp,v 1.27 2000/03/07 17:15:56 schmidt Exp") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms supporting + // POSIX aio calls. + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + +#include "ace/WIN32_Proactor.h" + +#elif defined (ACE_HAS_AIO_CALLS) + +#include "ace/POSIX_Proactor.h" + +#endif + + // Some debug helper functions + int DisableSignal ( int SigNum ); +int PrintSigMask (); + +#define COUT(X) cout << X ; cout.flush (); + +// Host that we're connecting to. +static ACE_TCHAR *host = 0; + +// duplex mode: ==0 half-duplex +// !=0 full duplex +static int duplex = 0 ; + +// number threads in the Proactor thread pool +static int nThreads = 1; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// Size of each initial asynchronous <read> operation. +static int initial_read_size = BUFSIZ; + + +#define MyMutex ACE_Recursive_Thread_Mutex +//#define MyMutex ACE_Thread_Mutex +//#define MyMutex ACE_Null_Mutex + +//-------------------------------------------------------------------------- +// MyTask plays role for Proactor threads pool +//-------------------------------------------------------------------------- +class MyTask: public ACE_Task<ACE_MT_SYNCH> +{ + +public: + + int svc (void) ; +}; + + +int MyTask::svc (void ) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) MyTask started\n")); + + while ( ACE_Proactor::event_loop_done () == 0 ) + { + ACE_Proactor::run_event_loop (); + } + + ACE_DEBUG ((LM_DEBUG, "(%t) MyTask finished\n")); + return 0 ; +} + +//----------------------------------------------------------- +// Receiver +//----------------------------------------------------------- +class Receiver : public ACE_Service_Handler +{ +public: + + Receiver (void); + ~Receiver (void); + + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + // This is called after the new connection has been accepted. + +protected: + // These methods are called by the framework + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result + &result); + // This is called when asynchronous <read> operation from the socket + // complete. + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result + &result); + // This is called when an asynchronous <write> to the file + // completes. + +private: + int initiate_read_stream (void); + int initiate_write_stream (ACE_Message_Block & mb, int nBytes ); + bool check_destroy () ; + + ACE_Asynch_Read_Stream rs_; + ACE_Asynch_Write_Stream ws_; + ACE_HANDLE handle_; + MyMutex m_Mtx ; + long nIOCount ; + static long nSessions ; +}; + + +long Receiver::nSessions = 0 ; + +Receiver::Receiver (void) + : handle_ (ACE_INVALID_HANDLE), + nIOCount ( 0 ) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + nSessions ++ ; + ACE_DEBUG ((LM_DEBUG, "Receiver Ctor nSessions=%d\n", nSessions )); +} + +Receiver::~Receiver (void) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + nSessions -- ; + ACE_OS::closesocket (this->handle_); + ACE_DEBUG ((LM_DEBUG, "~Receiver Dtor nSessions=%d\n", nSessions )); +} + +//--------------------------------------------------------------------- +// return true if we alive, false we commited suicide +// +//--------------------------------------------------------------------- +bool Receiver::check_destroy () +{ + { + ACE_Guard<MyMutex> locker (m_Mtx) ; + + if ( nIOCount > 0 ) + { + return true ; + } + } + + delete this ; + return false ; +} + + +void Receiver::open (ACE_HANDLE handle, + ACE_Message_Block &message_block) +{ + ACE_UNUSED_ARG (message_block); + + ACE_DEBUG ((LM_DEBUG, + "%N:%l:Receiver::open called\n")); + + + this->handle_ = handle; + + if (this->ws_.open (*this, this->handle_ ) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::open")); + + } + else if (this->rs_.open (*this, this->handle_) == -1) + { + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::open")); + } + else + { + initiate_read_stream (); + } + + + check_destroy (); +} + +int Receiver::initiate_read_stream (void) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + + // Create a new <Message_Block>. Note that this message block will + // be used both to <read> data asynchronously from the socket and to + // <write> data asynchronously to the file. + ACE_DEBUG ((LM_DEBUG, + "initiate_read_stream called\n")); + + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size ()- 1) == -1) + { + mb->release () ; + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::read"), + -1); + } + + nIOCount++ ; + return 0; +} + +int Receiver::initiate_write_stream (ACE_Message_Block & mb, int nBytes ) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + if (this->ws_.write (mb , nBytes ) == -1) + { + mb.release (); + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_File::write"), + -1); + } + + nIOCount++ ; + return 0; +} + +void +Receiver::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_read_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = + '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read + ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", + result.message_block ().rd_ptr ())); + + if ( result.success () && result.bytes_transferred () != 0) + { + // Successful read: write the data to the file asynchronously. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + + if(this->initiate_write_stream (result.message_block (), + + result.bytes_transferred () ) == 0 ) + { + if ( duplex != 0 ) + { + // Initiate new read from the stream. + this->initiate_read_stream () ; + } + } + } + else + { + result.message_block ().release (); + ACE_DEBUG ((LM_DEBUG, "Receiver completed\n")); + } + + { + ACE_Guard<MyMutex> locker (m_Mtx) ; + nIOCount-- ; + } + check_destroy () ; +} + +void +Receiver::handle_write_stream (const ACE_Asynch_Write_Stream::Result + &result) +{ + ACE_DEBUG ((LM_DEBUG, "handle_write_stream called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + + result.message_block ().release (); + + if (result.success ()) + { + // This code is not robust enough to deal with short file writes + // (which hardly ever happen) ;-) + //ACE_ASSERT (result.bytes_to_write () == result.bytes_transferred ()); + + if ( duplex == 0 ) + { + initiate_read_stream () ; + } + } + + { + ACE_Guard<MyMutex> locker (m_Mtx) ; + nIOCount-- ; + } + check_destroy () ; +} + +//------------------------------------------------------------------------- +// Sender: sends indefinetely welcome message +// and recieves it back +//------------------------------------------------------------------------ +class Sender : public ACE_Handler +{ +public: + Sender (void); + ~Sender (void); + int open (const ACE_TCHAR *host, u_short port); + void close (); + ACE_HANDLE handle (void) const; + void handle (ACE_HANDLE); + +protected: +// These methods are called by the freamwork + +virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result +&result); +// This is called when asynchronous reads from the socket complete + +virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result +&result); +// This is called when asynchronous writes from the socket complete + +private: + +int initiate_read_stream (void); +int initiate_write_stream (void); + +ACE_SOCK_Stream stream_; +// Network I/O handle + +ACE_Asynch_Write_Stream ws_; +// ws (write stream): for writing to the socket + +ACE_Asynch_Read_Stream rs_; +// rs (read file): for reading from the socket + +ACE_Message_Block welcome_message_; +// Welcome message + +MyMutex m_Mtx ; +long nIOCount ; +}; + +static char *data = "Welcome to Irfan World! Irfan RULES here !!\n"; + +Sender::Sender (void) + :nIOCount ( 0 ) +{ + // Moment of inspiration... :-) + this->welcome_message_.init (data, ACE_OS::strlen (data)); +} + +Sender::~Sender (void) +{ + close (); +} + +void Sender::close () +{ + this->stream_.close (); +} + +ACE_HANDLE Sender::handle (void) const +{ + return this->stream_.get_handle (); +} + +void Sender::handle (ACE_HANDLE handle) +{ + this->stream_.set_handle (handle); +} + +int Sender::open (const ACE_TCHAR *host, u_short port) +{ + // Initialize stuff + // Connect to remote host + ACE_INET_Addr address (port, host); + ACE_SOCK_Connector connector; + + if (connector.connect (this->stream_, + address) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_SOCK_Connector::connect"), + -1); + } + + // Open ACE_Asynch_Write_Stream + if (this->ws_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::open"), + -1); + + // Open ACE_Asynch_Read_Stream + if (this->rs_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_File::open"), + -1); + + // Start an asynchronous transmit file + if ( this->initiate_write_stream () == -1) + return -1; + + if ( duplex != 0 ) + { + // Start an asynchronous read file + if (this->initiate_read_stream () == -1) + return -1; + } + + return 0; +} + +int Sender::initiate_write_stream (void) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + + + welcome_message_.rd_ptr( welcome_message_.base ()); + welcome_message_.wr_ptr( welcome_message_.base ()); + welcome_message_.wr_ptr (ACE_OS::strlen (data)); + + if (this->ws_.write (welcome_message_, + welcome_message_.length () + ) == -1) + { + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_File::write"), + -1); + } + + nIOCount++ ; + return 0; +} + +int Sender::initiate_read_stream (void) +{ + ACE_Guard<MyMutex> locker (m_Mtx) ; + + // Create a new <Message_Block>. Note that this message block will + // be used both to <read> data asynchronously from the socket and to + // <write> data asynchronously to the file. + ACE_DEBUG ((LM_DEBUG, + "initiate_read_stream called\n")); + + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size ()- 1) == -1) + { + mb->release () ; + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::read"), + -1); + } + + nIOCount++ ; + return 0; +} + + +void Sender::handle_write_stream (const ACE_Asynch_Write_Stream::Result + &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_write_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr (result.message_block ().rd_ptr () - + result.bytes_transferred ()); + + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", + result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", + result.message_block ().rd_ptr ())); + + // Simplify just for Test + if (result.success () && result.bytes_transferred () != 0) + { + if ( duplex != 0 ) // full duplex, continue write + { + initiate_write_stream () ; + } + else // half-duplex read reply, after read we will start + // write + { + initiate_read_stream () ; + } + } + + { + ACE_Guard<MyMutex> locker (m_Mtx) ; + nIOCount-- ; + } +} + +void +Sender::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_read_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = + '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read + ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", + result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) + result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", + result.message_block ().rd_ptr ())); + + result.message_block().release (); + + if ( result.success () && result.bytes_transferred () != 0) + { + // Successful read: write the data to the file asynchronously. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + + if ( duplex != 0 ) // full duplex, continue read + { + initiate_read_stream () ; + } + else // half-duplex writey, after write we will start read + { + initiate_write_stream () ; + } + } + + { + ACE_Guard<MyMutex> locker (m_Mtx) ; + nIOCount-- ; + } +} + +//-------------------------------------------------------------------------- + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("n:p:d:h:")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'h': + host = get_opt.opt_arg (); + break; + case 'n': + nThreads = ACE_OS::atoi (get_opt.opt_arg ()) ; + break; + case 'p': + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'd': + duplex = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR ((LM_ERROR, "%p.\n", + "usage :\n" + "-h <host> for Sender mode\n" + "-d <duplex mode 1-on/0-off>\n" + "-p <port to listen/connect>\n" + "-n <number threads for Proactor pool>\n")); + return -1; + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + ACE_UNUSED_ARG (initial_read_size); + + if (parse_args (argc, argv) == -1) + return -1; + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + + ACE_WIN32_Proactor * pImpl = new ACE_WIN32_Proactor; + +#elif defined (ACE_HAS_AIO_CALLS) + + // ACE_POSIX_AIOCB_Proactor * pImpl = new ACE_POSIX_AIOCB_Proactor; + ACE_POSIX_SIG_Proactor * pImpl = new ACE_POSIX_SIG_Proactor; +#endif + + ACE_Proactor Proactor ( pImpl ,1 ); + + ACE_Proactor::instance( & Proactor ); + + + MyTask Task1 ; + + if (Task1.activate (THR_NEW_LWP, nThreads ) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); + } + + Sender sender; + ACE_Asynch_Acceptor<Receiver> acceptor; + + int Rc = -1 ; + + if ( host == NULL ) // Acceptor + { + // Simplify , initial read with zero size + Rc = acceptor.open (ACE_INET_Addr (port),0,1); + + } + else + { + Rc = sender.open (host, port); + } + + if ( Rc == 0 ) + { + char c ; + cout << "Press any key to stop and exit=>\n" << flush ; + cin.clear (); + cin >> c ; + } + + ACE_Proactor::end_event_loop () ; + + if ( host != NULL ) // we are sender + { + sender.close () ; // disconnect to get reciever error !!! + } + + + ACE_Thread_Manager * pTM = ACE_Thread_Manager::instance(); + + pTM->wait_task ( & Task1 ) ; + + ACE_Proactor::instance( ( ACE_Proactor* )NULL ); + + return 0; +} +//-------------------------------------------------------------------- +// +//-------------------------------------------------------------------- +int DisableSignal ( int SigNum ) +{ + +#ifndef ACE_WIN32 + sigset_t signal_set; + if ( sigemptyset (&signal_set) == - 1 ) + { + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "sigemptyset failed")); + } + + sigaddset (&signal_set, SigNum); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + { + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "pthread_sigmask failed")); + } +#else + ACE_UNUSED_ARG(SigNum); +#endif + + return 1; +} +//-------------------------------------------------------------------- +// Get the <signal_set> back from the OS. +//-------------------------------------------------------------------- + +int PrintSigMask () +{ +#ifndef ACE_WIN32 + + sigset_t mask ; + int member = 0; + + COUT ( "\n=============Signal Mask==========" ) + + if (ACE_OS::pthread_sigmask (SIG_SETMASK, 0, & mask ) != 0) + { + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "ACE_OS::pthread_sigmask failed")); + } + else for (int i = 1 ; i < 1000; i++) + { + member = sigismember (&mask,i); + + COUT ( "\nSig " ) + COUT ( i ) + COUT ( " is " ) + COUT (member ) + + if (member == -1) + { + break ; + } + } +#endif + return 0; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_proactor3.cpp b/ACE/examples/Reactor/Proactor/test_proactor3.cpp new file mode 100644 index 00000000000..c47468276c8 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_proactor3.cpp @@ -0,0 +1,864 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_proactor3.cpp +// +// = DESCRIPTION +// This program illustrates how the <ACE_Proactor> can be used to +// implement an application that does various asynchronous +// operations. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// modified by Alexander Libman <alibman@baltimore.com> +// from original test_proactor.cpp +// ============================================================================ + +#include "ace/Signal.h" + +#include "ace/Service_Config.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/Asynch_IO_Impl.h" +#include "ace/Asynch_Acceptor.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/SOCK_Stream.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" + +// FUZZ: disable check_for_streams_include +#include "ace/streams.h" + +#include "ace/Task.h" + +ACE_RCSID(Proactor, test_proactor, "test_proactor.cpp,v 1.27 2000/03/07 17:15:56 schmidt Exp") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) + // This only works on Win32 platforms and on Unix platforms + // supporting POSIX aio calls. + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + +# include "ace/WIN32_Proactor.h" + +#elif defined (ACE_HAS_AIO_CALLS) + +# include "ace/POSIX_Proactor.h" +# include "ace/SUN_Proactor.h" + +#endif /* defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) */ + +// Some debug helper functions +static int disable_signal (int sigmin, int sigmax); +#if 0 +static int print_sigmask (void); +#endif + +#define COUT(X) cout << X; cout.flush (); + +// Proactor Type (UNIX only, Win32 ignored) 0-default, 1 -AIOCB, +// 2-SIG, 3-SUN +static int proactor_type = 0; + +// POSIX : > 0 max number aio operations proactor, +static int max_aio_operations = 0; + +// Host that we're connecting to. +static ACE_TCHAR *host = 0; + +// number of Senders instances +static int senders = 1; +static const int MaxSenders = 100; + +// duplex mode: ==0 half-duplex +// !=0 full duplex +static int duplex = 0; + +// number threads in the Proactor thread pool +static int threads = 1; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +class MyTask: public ACE_Task<ACE_MT_SYNCH> +{ + // = TITLE + // MyTask plays role for Proactor threads pool +public: + MyTask (void) : threads_ (0), proactor_ (0) {} + + int svc (void); + void waitready (void) { event_.wait (); } + +private: + ACE_Recursive_Thread_Mutex mutex_; + int threads_; + ACE_Proactor *proactor_; + ACE_Manual_Event event_; + + void create_proactor (void); + void delete_proactor (void); +}; + +void +MyTask::create_proactor (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + if (threads_ == 0) + { +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + ACE_WIN32_Proactor *proactor = new ACE_WIN32_Proactor; + ACE_DEBUG ((LM_DEBUG,"(%t) Create Proactor Type=WIN32")); + +#elif defined (ACE_HAS_AIO_CALLS) + + ACE_POSIX_Proactor *proactor = 0; + + switch (proactor_type) + { + case 1: proactor = new ACE_POSIX_AIOCB_Proactor (max_aio_operations); + ACE_DEBUG ((LM_DEBUG,"(%t) Create Proactor Type=AIOCB\n")); + break; + case 2: proactor = new ACE_POSIX_SIG_Proactor; + ACE_DEBUG ((LM_DEBUG,"(%t) Create Proactor Type=SIG\n")); + break; +# if defined (sun) + case 3: proactor = new ACE_SUN_Proactor (max_aio_operations); + ACE_DEBUG ((LM_DEBUG,"(%t) Create Proactor Type=SUN\n")); + break; +# endif /* sun */ + default:proactor = new ACE_POSIX_SIG_Proactor; + ACE_DEBUG ((LM_DEBUG,"(%t) Create Proactor Type=SIG\n")); + break; + } +#endif + + proactor_ = new ACE_Proactor (proactor, 1); + + ACE_Proactor::instance(proactor_); + event_.signal (); + } + + threads_++; +} + +void +MyTask::delete_proactor (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + if (--threads_ == 0) + { + ACE_DEBUG ((LM_DEBUG, "(%t) Delete Proactor\n")); + ACE_Proactor::instance ((ACE_Proactor *) 0); + delete proactor_; + proactor_ = 0; + } +} + +int +MyTask::svc (void) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) MyTask started\n")); + + create_proactor (); + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + + while (ACE_Proactor::event_loop_done () == 0) + ACE_Proactor::run_event_loop (); + + delete_proactor (); + + ACE_DEBUG ((LM_DEBUG, "(%t) MyTask finished\n")); + return 0; +} + +class Receiver : public ACE_Service_Handler +{ +public: + + Receiver (void); + ~Receiver (void); + + virtual void open (ACE_HANDLE handle, + ACE_Message_Block &message_block); + // This is called after the new connection has been accepted. + + static long get_number_sessions (void) { return sessions_; } + +protected: + // These methods are called by the framework + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This is called when asynchronous <read> operation from the socket + // complete. + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This is called when an asynchronous <write> to the file + // completes. + +private: + int initiate_read_stream (void); + int initiate_write_stream (ACE_Message_Block & mb, int nBytes); + int check_destroy (void); + + ACE_Asynch_Read_Stream rs_; + ACE_Asynch_Write_Stream ws_; + ACE_HANDLE handle_; + ACE_Recursive_Thread_Mutex mutex_; + long io_count_; + static long sessions_; +}; + +long Receiver::sessions_ = 0; + +Receiver::Receiver (void) + : handle_ (ACE_INVALID_HANDLE), + io_count_ (0) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + sessions_++; + ACE_DEBUG ((LM_DEBUG, "Receiver Ctor sessions_=%d\n", sessions_)); +} + +Receiver::~Receiver (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + sessions_--; + ACE_OS::closesocket (this->handle_); + ACE_DEBUG ((LM_DEBUG, "~Receiver Dtor sessions_=%d\n", sessions_)); +} + +// return true if we alive, false we commited suicide +int +Receiver::check_destroy (void) +{ + { + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + if (io_count_ > 0) + return 1; + } + + delete this; + return 0; +} + +void +Receiver::open (ACE_HANDLE handle, + ACE_Message_Block &) +{ + ACE_DEBUG ((LM_DEBUG, + "%N:%l:Receiver::open called\n")); + + this->handle_ = handle; + + if (this->ws_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::open")); + else if (this->rs_.open (*this, this->handle_) == -1) + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::open")); + else + initiate_read_stream (); + + check_destroy (); +} + +int +Receiver::initiate_read_stream (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size ()- 1) == -1) + { + mb->release (); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::read"), + -1); + } + + io_count_++; + return 0; +} + +int +Receiver::initiate_write_stream (ACE_Message_Block &mb, int nbytes) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + if (nbytes <= 0) + { + mb.release (); + ACE_ERROR_RETURN((LM_ERROR, + "ACE_Asynch_Write_Stream::write nbytes <0 "), + -1); + } + + if (this->ws_.write (mb, nbytes) == -1) + { + mb.release (); + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::write"), + -1); + } + + io_count_++; + return 0; +} + +void +Receiver::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + // Reset pointers. + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + if (result.bytes_transferred () == 0 || result.error () != 0) + { + ACE_DEBUG ((LM_DEBUG, "handle_read_stream called\n")); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); + } + + if (result.success () && result.bytes_transferred () != 0) + { + // Successful read: write the data to the file asynchronously. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + + if(this->initiate_write_stream (result.message_block (), + result.bytes_transferred ()) == 0) + { + if (duplex != 0) + { + // Initiate new read from the stream. + this->initiate_read_stream (); + } + } + } + else + { + result.message_block ().release (); + ACE_DEBUG ((LM_DEBUG, "Receiver completed\n")); + } + + { + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + io_count_--; + } + check_destroy (); +} + +void +Receiver::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + if (result.bytes_transferred () == 0 || result.error () != 0) + { + ACE_DEBUG ((LM_DEBUG, "handle_write_stream called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + } + + result.message_block ().release (); + + if (result.success () && result.bytes_transferred () != 0) + { + // This code is not robust enough to deal with short file writes + // (which hardly ever happen);-) + // ACE_ASSERT (result.bytes_to_write () == result.bytes_transferred ()); + + if (duplex == 0) + initiate_read_stream (); + } + + { + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + io_count_--; + } + check_destroy (); +} + +class Sender : public ACE_Handler +{ + // = TITLE + // Sends welcome messages receives them back. +public: + Sender (void); + ~Sender (void); + int open (const ACE_TCHAR *host, u_short port); + void close (void); + ACE_HANDLE handle (void) const; + +protected: + // These methods are called by the freamwork + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This is called when asynchronous reads from the socket complete + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This is called when asynchronous writes from the socket complete + +private: + + int initiate_read_stream (void); + int initiate_write_stream (void); + + ACE_SOCK_Stream stream_; + // Network I/O handle + + ACE_Asynch_Write_Stream ws_; + // ws (write stream): for writing to the socket + + ACE_Asynch_Read_Stream rs_; + // rs (read file): for reading from the socket + + ACE_Message_Block welcome_message_; + // Welcome message + + ACE_Recursive_Thread_Mutex mutex_; + long io_count_; +}; + +static char *data = "Welcome to Irfan World! Irfan RULES here !!\n"; + +Sender::Sender (void) + : io_count_ (0) +{ + // Moment of inspiration... :-) + this->welcome_message_.init (data, ACE_OS::strlen (data)); +} + +Sender::~Sender (void) +{ + close (); +} + +void Sender::close (void) +{ + this->stream_.close (); +} + +ACE_HANDLE Sender::handle (void) const +{ + return this->stream_.get_handle (); +} + +int Sender::open (const ACE_TCHAR *host, u_short port) +{ + // Initialize stuff + // Connect to remote host + ACE_INET_Addr address (port, host); + ACE_SOCK_Connector connector; + + if (connector.connect (this->stream_, + address) == -1) + { + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_SOCK_Connector::connect"), + -1); + } + + // Open ACE_Asynch_Write_Stream + if (this->ws_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::open"), + -1); + + // Open ACE_Asynch_Read_Stream + if (this->rs_.open (*this) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::open"), + -1); + + // Start an asynchronous transmit file + if (this->initiate_write_stream () == -1) + return -1; + + if (duplex != 0) + // Start an asynchronous read file + if (this->initiate_read_stream () == -1) + return -1; + + return 0; +} + +int +Sender::initiate_write_stream (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + welcome_message_.rd_ptr(welcome_message_.base ()); + welcome_message_.wr_ptr(welcome_message_.base ()); + welcome_message_.wr_ptr (ACE_OS::strlen (data)); + + if (this->ws_.write (welcome_message_, + welcome_message_.length ()) == -1) + ACE_ERROR_RETURN((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Stream::write"), + -1); + io_count_++; + return 0; +} + +int +Sender::initiate_read_stream (void) +{ + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + + // Create a new <Message_Block>. Note that this message block will + // be used both to <read> data asynchronously from the socket and to + // <write> data asynchronously to the file. + ACE_DEBUG ((LM_DEBUG, + "initiate_read_stream called\n")); + + ACE_Message_Block *mb = 0; + ACE_NEW_RETURN (mb, + ACE_Message_Block (BUFSIZ + 1), + -1); + + // Inititiate read + if (this->rs_.read (*mb, mb->size ()- 1) == -1) + { + mb->release (); + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Stream::read"), + -1); + } + + io_count_++; + return 0; +} + +void +Sender::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + if (result.bytes_transferred () == 0 || result.error () != 0) + { + ACE_DEBUG ((LM_DEBUG, "handle_write_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr (result.message_block ().rd_ptr () - result.bytes_transferred ()); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); + } + + // Simplify just for Test + if (result.success () && result.bytes_transferred () != 0) + { + if (duplex != 0) // full duplex, continue write + initiate_write_stream (); + else // half-duplex read reply, after read we will start write + initiate_read_stream (); + } + + { + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + io_count_--; + } +} + +void +Sender::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + if (result.bytes_transferred () == 0 || result.error () != 0) + { + ACE_DEBUG ((LM_DEBUG, + "handle_read_stream called\n")); + + // Reset pointers. + result.message_block ().rd_ptr ()[result.bytes_transferred ()] = '\0'; + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "act", (u_long) result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "completion_key", (u_long) result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "message_block", result.message_block ().rd_ptr ())); + } + + result.message_block().release (); + + if (result.success () && result.bytes_transferred () != 0) + { + // Successful read: write the data to the file asynchronously. + // Note how we reuse the <ACE_Message_Block> for the writing. + // Therefore, we do not delete this buffer because it is handled + // in <handle_write_stream>. + + if (duplex != 0) // full duplex, continue read + initiate_read_stream (); + else // half-duplex writey, after write we will start read + initiate_write_stream (); + } + + { + ACE_Guard<ACE_Recursive_Thread_Mutex> locker (mutex_); + io_count_--; + } +} + +static int +set_proactor_type (const char *ptype) +{ + if (!ptype) + return false; + + switch (toupper (*ptype)) + { + case 'D' : proactor_type = 0; return true; + case 'A' : proactor_type = 1; return true; + case 'I' : proactor_type = 2; return true; +#if defined (sun) + case 'S' : proactor_type = 3; return true; +#endif /* sun */ + } + return false; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("t:o:n:p:d:h:s:u")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'd': // duplex + duplex = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': // host for sender + host = get_opt.opt_arg (); + break; + case 'p': // port number + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'n': // thread pool size + threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': // number of senders + senders = ACE_OS::atoi (get_opt.opt_arg ()); + if (senders > MaxSenders) + senders = MaxSenders; + break; + case 'o': // max number of aio for proactor + max_aio_operations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 't': // Proactor Type + if (set_proactor_type (get_opt.opt_arg ())) + break; + case 'u': + default: + ACE_ERROR ((LM_ERROR, "%p.", + "\nusage:" + "\n-o <max number of started aio operations for Proactor>" + "\n-t <Proactor type> UNIX-only, Win32-default always:" + "\n a AIOCB" + "\n i SIG" + "\n s SUN" + "\n d default" + "\n-d <duplex mode 1-on/0-off>" + "\n-h <host> for Sender mode" + "\n-n <number threads for Proactor pool>" + "\n-p <port to listen/connect>" + "\n-s <number of sender's instances>" + "\n-u show this message" + "\n")); + + return -1; + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ +#if defined (sun) + ACE_DEBUG ((LM_DEBUG, "\nSUN defined!\n")); +#endif + if (parse_args (argc, argv) == -1) + return -1; + + disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX); + + MyTask task1; + + if (task1.activate (THR_NEW_LWP, threads) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p.\n", + "main"), + -1); + + // wait for creation of Proactor + task1.waitready (); + + Sender * send_list[MaxSenders]; + + ACE_Asynch_Acceptor<Receiver> acceptor; + + int rc = -1; + int i; + char c; + + if (host == 0) // Acceptor + { + // Simplify, initial read with zero size + if (acceptor.open (ACE_INET_Addr (port),0,1) == 0) + rc = 1; + } + else + { + for (i = 0; i < senders; ++i) + send_list[i] = new Sender; + + for (i = 0; i < senders; ++i) + if (send_list[i]->open (host, port) == 0) + rc++; + } + + if (rc > 0) + { + cout << "Press any key to stop=>" << flush; + cin.clear (); + cin >> c; + } + + ACE_Proactor::end_event_loop (); + + if (host != 0) // we are sender + { + for (i = 0; i < senders; ++i) + send_list[i]->close (); + } + + + ACE_Thread_Manager *tm = + ACE_Thread_Manager::instance(); + + tm->wait_task (&task1); + + cout << "\nNumber of Receivers objects=" + << Receiver::get_number_sessions () + << flush; + + for (i = 0; i < senders; ++i) + { + delete (send_list[i]); + send_list[i] = 0; + } + + return 0; +} + +static int +disable_signal (int sigmin, int sigmax) +{ +#ifndef ACE_WIN32 + + sigset_t signal_set; + if (sigemptyset (&signal_set) == - 1) + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "sigemptyset failed")); + + for (int i = sigmin; i <= sigmax; i++) + sigaddset (&signal_set, i); + + // Put the <signal_set>. + if (ACE_OS::pthread_sigmask (SIG_BLOCK, &signal_set, 0) != 0) + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "pthread_sigmask failed")); +#endif /* ACE_WIN32 */ + + return 1; +} + +// Get the <signal_set> back from the OS. + +#if 0 +static int +print_sigmask (void) +{ +#ifndef ACE_WIN32 + sigset_t mask; + int member = 0; + + COUT ("\n=============Signal Mask==========") + + if (ACE_OS::pthread_sigmask (SIG_SETMASK, 0, & mask) != 0) + ACE_ERROR ((LM_ERROR, + "Error:(%P | %t):%p\n", + "ACE_OS::pthread_sigmask failed")); + else + for (int i = 1; i < 1000; i++) + { + member = sigismember (&mask,i); + + COUT ("\nSig ") + COUT (i) + COUT (" is ") + COUT (member) + + if (member == -1) + break; + } + +#endif /* ACE_WIN32 */ + return 0; +} +#endif /* 0 */ + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_timeout.cpp b/ACE/examples/Reactor/Proactor/test_timeout.cpp new file mode 100644 index 00000000000..39351717db9 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_timeout.cpp @@ -0,0 +1,130 @@ +// $Id: test_timeout.cpp + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_timeout.cpp +// +// = DESCRIPTION +// +// This example application shows how to write event loops that +// handle events for some fixed amount of time. Note that any +// thread in the Proactor thread pool can call back the handler. On +// POSIX4 systems, this test works only with POSIX_SIG_Proactor, +// which can work with multiple threads. +// +// = AUTHOR +// Irfan Pyarali and Alexander Babu Arulanthu +// +// ============================================================================ + +#include "ace/Proactor.h" +#include "ace/Task.h" +#include "ace/Atomic_Op.h" +#include "ace/OS_NS_sys_time.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_main.h" + +ACE_RCSID(Proactor, test_timeout, "$Id$") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || \ + (defined (ACE_HAS_AIO_CALLS)) && !defined (ACE_POSIX_AIOCB_PROACTOR)) + // This only works on Win32 platforms and on Unix platforms supporting + // POSIX aio calls. + +class Timeout_Handler : public ACE_Handler +{ + // = TITLE + // Generic timeout handler. +public: + Timeout_Handler (void) + : start_time_ (ACE_OS::gettimeofday ()) + { + } + + virtual void handle_time_out (const ACE_Time_Value &tv, + const void *arg) + { + // Print out when timeouts occur. + ACE_DEBUG ((LM_DEBUG, "(%t) %d timeout occurred for %s @ %d.\n", + ++count_, + (char *) arg, + (tv - this->start_time_).sec ())); + + // Sleep for a while + ACE_OS::sleep (4); + } + +private: + ACE_Atomic_Op <ACE_SYNCH_MUTEX, int> count_; + // Number of the timer event. + + ACE_Time_Value start_time_; + // Starting time of the test. +}; + +class Worker : public ACE_Task <ACE_NULL_SYNCH> +{ +public: + int svc (void) + { + // Handle events for 13 seconds. + ACE_Time_Value run_time (13); + + ACE_DEBUG ((LM_DEBUG, "(%t):Starting svc routine\n")); + + if (ACE_Proactor::run_event_loop(run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "(%t):%p.\n", "Worker::svc"), -1); + + ACE_DEBUG ((LM_DEBUG, "(%t) work complete\n")); + + return 0; + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Timeout_Handler handler; + + // Register a 2 second timer. + ACE_Time_Value foo_tv (2); + if (ACE_Proactor::instance ()->schedule_timer (handler, + (void *) "Foo", + ACE_Time_Value::zero, + foo_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + // Register a 3 second timer. + ACE_Time_Value bar_tv (3); + if (ACE_Proactor::instance ()->schedule_timer (handler, + (void *) "Bar", + ACE_Time_Value::zero, + bar_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + Worker worker; + + if (worker.activate (THR_NEW_LWP, 10) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); + + ACE_Thread_Manager::instance ()->wait (); + + return 0; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ + +int +main (int, char *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example is multithreaded version of test_timeout_st.cpp\n" + "This doesnt work on this platform !!!\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS && !ACE_POSIX_AIOCB_PROACTOR*/ diff --git a/ACE/examples/Reactor/Proactor/test_timeout_st.cpp b/ACE/examples/Reactor/Proactor/test_timeout_st.cpp new file mode 100644 index 00000000000..ae44c2ba1f4 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_timeout_st.cpp @@ -0,0 +1,99 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_timeout_st.cpp +// +// = DESCRIPTION +// +// This example application shows how to write event loops that +// handle events for some fixed amount of time. This is the single +// threaded version of the test_timeout.cpp application. +// +// = AUTHOR +// Irfan Pyarali and Alexander Babu Arulanthu +// +// ============================================================================ + +#include "ace/Proactor.h" +#include "ace/OS_main.h" + +ACE_RCSID(Proactor, test_timeout, "$Id$") + +#if ((defined (ACE_WIN32) && !defined (ACE_HAS_WINCE)) || (defined (ACE_HAS_AIO_CALLS))) +// This only works on Win32 platforms and on Unix platforms supporting +// POSIX aio calls. + +class Timeout_Handler : public ACE_Handler +{ + // = TITLE + // Generic timeout handler. + +public: + Timeout_Handler (void) + : count_ (0), + start_time_ (ACE_OS::gettimeofday ()) + { + } + + virtual void handle_time_out (const ACE_Time_Value &tv, + const void *arg) + { + // Print out when timeouts occur. + ACE_DEBUG ((LM_DEBUG, "(%t) %d timeout occurred for %s @ %d.\n", + ++count_, + (char *) arg, + (tv - this->start_time_).sec ())); + } + +private: + int count_; + // Sequence number for the timeouts. + + ACE_Time_Value start_time_; + // Starting time of the test. +}; + + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Timeout_Handler handler; + + // Register a 2 second timer. + ACE_Time_Value foo_tv (2); + if (ACE_Proactor::instance ()->schedule_timer (handler, + (void *) "Foo", + ACE_Time_Value::zero, + foo_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + // Register a 3 second timer. + ACE_Time_Value bar_tv (3); + if (ACE_Proactor::instance ()->schedule_timer (handler, + (void *) "Bar", + ACE_Time_Value::zero, + bar_tv) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + // Handle events for 13 seconds. + ACE_Time_Value run_time (13); + + ACE_DEBUG ((LM_DEBUG, "Starting event loop\n")); + + // Run the event loop. + if (ACE_Proactor::run_event_loop(run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "(%t):%p.\n", "Worker::svc"), + 1); + + ACE_DEBUG ((LM_DEBUG, "Ending event loop\n")); + + return 0; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ diff --git a/ACE/examples/Reactor/Proactor/test_udp_proactor.cpp b/ACE/examples/Reactor/Proactor/test_udp_proactor.cpp new file mode 100644 index 00000000000..49d834a2884 --- /dev/null +++ b/ACE/examples/Reactor/Proactor/test_udp_proactor.cpp @@ -0,0 +1,432 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// test_udp_proactor.cpp +// +// = DESCRIPTION +// This program illustrates how the <ACE_Proactor> can be used to +// implement an application that does asynchronous operations using +// datagrams. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> and +// Roger Tragin <r.tragin@computer.org> +// +// ============================================================================ + +#include "ace/OS_NS_string.h" +#include "ace/OS_main.h" +#include "ace/Proactor.h" +#include "ace/Asynch_IO.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Dgram.h" +#include "ace/Message_Block.h" +#include "ace/Get_Opt.h" +#include "ace/Log_Msg.h" + +ACE_RCSID(Proactor, test_udp_proactor, "test_proactor.cpp,v 1.29 2001/02/02 23:41:16 shuston Exp") + +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) || defined (ACE_HAS_AIO_CALLS) + // This only works on Win32 platforms. + +// Host that we're connecting to. +static ACE_TCHAR *host = 0; + +// Port that we're receiving connections on. +static u_short port = ACE_DEFAULT_SERVER_PORT; + +// Keep track of when we're done. +static int done = 0; + +class Receiver : public ACE_Service_Handler +{ + // = TITLE + // This class will receive data from + // the network connection and dump it to a file. +public: + // = Initialization and termination. + Receiver (void); + ~Receiver (void); + + int open_addr (const ACE_INET_Addr &localAddr); + +protected: + // These methods are called by the framework + + /// This method will be called when an asynchronous read completes on + /// a UDP socket. + virtual void handle_read_dgram (const ACE_Asynch_Read_Dgram::Result &result); + +private: + ACE_SOCK_Dgram sock_dgram_; + + ACE_Asynch_Read_Dgram rd_; + // rd (read dgram): for reading from a UDP socket. + const char* completion_key_; + const char* act_; +}; + +Receiver::Receiver (void) + : completion_key_ ("Receiver Completion Key"), + act_ ("Receiver ACT") +{ +} + +Receiver::~Receiver (void) +{ + sock_dgram_.close (); +} + +int +Receiver::open_addr (const ACE_INET_Addr &localAddr) +{ + ACE_DEBUG ((LM_DEBUG, + "%N:%l:Receiver::open_addr called\n")); + + // Create a local UDP socket to receive datagrams. + if (this->sock_dgram_.open (localAddr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_SOCK_Dgram::open"), -1); + + // Initialize the asynchronous read. + if (this->rd_.open (*this, + this->sock_dgram_.get_handle (), + this->completion_key_, + ACE_Proactor::instance ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Dgram::open"), -1); + + // Create a buffer to read into. We are using scatter/gather to + // read the message header and message body into 2 buffers + + // create a message block to read the message header + ACE_Message_Block* msg = 0; + ACE_NEW_RETURN (msg, ACE_Message_Block (1024), -1); + + // the next line sets the size of the header, even though we + // allocated a the message block of 1k, by setting the size to 20 + // bytes then the first 20 bytes of the reveived datagram will be + // put into this message block. + msg->size (20); // size of header to read is 20 bytes + + // create a message block to read the message body + ACE_Message_Block* body = 0; + ACE_NEW_RETURN (body, ACE_Message_Block (1024), -1); + // The message body will not exceed 1024 bytes, at least not in this test. + + // set body as the cont of msg. This associates the 2 message + // blocks so that a read will fill the first block (which is the + // header) up to size (), and use the cont () block for the rest of + // the data. You can chain up to IOV_MAX message block using this + // method. + msg->cont (body); + + // ok lets do the asynch read + size_t number_of_bytes_recvd = 0; + + int res = rd_.recv (msg, + number_of_bytes_recvd, + 0, + PF_INET, + this->act_); + switch (res) + { + case 0: + // this is a good error. The proactor will call our handler when the + // read has completed. + break; + case 1: + // actually read something, we will handle it in the handler callback + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, + "%s = %d\n", + "bytes recieved immediately", + number_of_bytes_recvd)); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + res = 0; + break; + case -1: + // Something else went wrong. + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Dgram::recv")); + // the handler will not get called in this case so lets clean up our msg + msg->release (); + break; + default: + // Something undocumented really went wrong. + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Read_Dgram::recv")); + msg->release (); + break; + } + + return res; +} + +void +Receiver::handle_read_dgram (const ACE_Asynch_Read_Dgram::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_read_dgram called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_read", result.bytes_to_read ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_INET_Addr peerAddr; + result.remote_address (peerAddr); + ACE_DEBUG ((LM_DEBUG, "%s = %s:%d\n", "peer_address", peerAddr.get_host_addr (), peerAddr.get_port_number ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "flags", result.flags ())); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "act", result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "completion_key", result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + + if (result.success () && result.bytes_transferred () != 0) + { + // loop through our message block and print out the contents + for (const ACE_Message_Block* msg = result.message_block (); msg != 0; msg = msg->cont ()) + { // use msg->length () to get the number of bytes written to the message + // block. + ACE_DEBUG ((LM_DEBUG, "Buf=[size=<%d>", msg->length ())); + for (u_long i = 0; i < msg->length (); ++i) + ACE_DEBUG ((LM_DEBUG, + "%c", (msg->rd_ptr ())[i])); + ACE_DEBUG ((LM_DEBUG, "]\n")); + } + } + + ACE_DEBUG ((LM_DEBUG, + "Receiver completed\n")); + + // No need for this message block anymore. + result.message_block ()->release (); + + // Note that we are done with the test. + done++; +} + +class Sender : public ACE_Handler +{ + // = TITLE + // The class will be created by <main>. +public: + Sender (void); + ~Sender (void); + int open (const ACE_TCHAR *host, u_short port); + +protected: + // These methods are called by the freamwork + + virtual void handle_write_dgram (const ACE_Asynch_Write_Dgram::Result &result); + // This is called when asynchronous writes from the dgram socket + // complete + +private: + + ACE_SOCK_Dgram sock_dgram_; + // Network I/O handle + + ACE_Asynch_Write_Dgram wd_; + // wd (write dgram): for writing to the socket + + const char* completion_key_; + const char* act_; +}; + +Sender::Sender (void) + : completion_key_ ("Sender completion key"), + act_ ("Sender ACT") +{ +} + +Sender::~Sender (void) +{ + this->sock_dgram_.close (); +} + +int +Sender::open (const ACE_TCHAR *host, + u_short port) +{ + // Initialize stuff + + if (this->sock_dgram_.open (ACE_INET_Addr::sap_any) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_SOCK_Dgram::open"), -1); + + // Initialize the asynchronous read. + if (this->wd_.open (*this, + this->sock_dgram_.get_handle (), + this->completion_key_, + ACE_Proactor::instance ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Dgram::open"), -1); + + // We are using scatter/gather to send the message header and + // message body using 2 buffers + + // create a message block for the message header + ACE_Message_Block* msg = 0; + ACE_NEW_RETURN (msg, ACE_Message_Block (100), -1); + const char raw_msg [] = "To be or not to be."; + // Copy buf into the Message_Block and update the wr_ptr (). + msg->copy (raw_msg, ACE_OS::strlen (raw_msg) + 1); + + // create a message block for the message body + ACE_Message_Block* body = 0; + ACE_NEW_RETURN (body, ACE_Message_Block (100), -1); + ACE_OS::memset (body->wr_ptr (), 'X', 100); + body->wr_ptr (100); // always remember to update the wr_ptr () + + // set body as the cont of msg. This associates the 2 message blocks so + // that a send will send the first block (which is the header) up to + // length (), and use the cont () to get the next block to send. You can + // chain up to IOV_MAX message block using this method. + msg->cont (body); + + // do the asynch send + size_t number_of_bytes_sent = 0; + ACE_INET_Addr serverAddr (port, host); + int res = this->wd_.send (msg, number_of_bytes_sent, 0, serverAddr, this->act_); + + switch (res) + { + case 0: + // this is a good error. The proactor will call our handler when the + // send has completed. + break; + case 1: + // actually sent something, we will handle it in the handler callback + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, + "%s = %d\n", + "bytes sent immediately", + number_of_bytes_sent)); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + res = 0; + break; + case -1: + // Something else went wrong. + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Dgram::recv")); + // the handler will not get called in this case so lets clean up our msg + msg->release (); + break; + default: + // Something undocumented really went wrong. + ACE_ERROR ((LM_ERROR, + "%p\n", + "ACE_Asynch_Write_Dgram::recv")); + msg->release (); + break; + } + return res; +} + +void +Sender::handle_write_dgram (const ACE_Asynch_Write_Dgram::Result &result) +{ + ACE_DEBUG ((LM_DEBUG, + "handle_write_dgram called\n")); + + ACE_DEBUG ((LM_DEBUG, "********************\n")); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_to_write", result.bytes_to_write ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "handle", result.handle ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "bytes_transfered", result.bytes_transferred ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "flags", result.flags ())); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "act", result.act ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "success", result.success ())); + ACE_DEBUG ((LM_DEBUG, "%s = %s\n", "completion_key", result.completion_key ())); + ACE_DEBUG ((LM_DEBUG, "%s = %d\n", "error", result.error ())); + ACE_DEBUG ((LM_DEBUG, "********************\n")); + + + ACE_DEBUG ((LM_DEBUG, + "Sender completed\n")); + + // No need for this message block anymore. + result.message_block ()->release (); + + // Note that we are done with the test. + done++; +} + +static int +parse_args (int argc, ACE_TCHAR *argv[]) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT ("h:p:")); + int c; + + while ((c = get_opt ()) != EOF) + switch (c) + { + case 'h': + host = get_opt.opt_arg (); + break; + case 'p': + port = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", + "usage :\n" + "-h <host>\n"), -1); + } + + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + if (parse_args (argc, argv) == -1) + return -1; + + Sender sender; + + Receiver receiver; + + // If passive side + if (host == 0) + { + if (receiver.open_addr (ACE_INET_Addr (port)) == -1) + return -1; + } + // If active side + else if (sender.open (host, port) == -1) + return -1; + + for (int success = 1; + success > 0 && !done; + ) + // Dispatch events via Proactor singleton. + success = ACE_Proactor::instance ()->handle_events (); + + return 0; +} + +#else /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_DEBUG ((LM_DEBUG, + "This example does not work on this platform.\n")); + return 1; +} + +#endif /* ACE_WIN32 && !ACE_HAS_WINCE || ACE_HAS_AIO_CALLS*/ + diff --git a/ACE/examples/Reactor/README b/ACE/examples/Reactor/README new file mode 100644 index 00000000000..fefaeeaf317 --- /dev/null +++ b/ACE/examples/Reactor/README @@ -0,0 +1,20 @@ +This directory contains subdirectories that test the ACE Reactor and Proactor + + . Dgram + Tests the CODgram and Dgram classes with the Reactor. + + . Misc + Various miscellaneous tests of Reactor functionality + (e.g., signals, timers, notification, etc.). + + . Multicast + Tests out the ACE multicast capabilities in conjunction + with the Reactor. + + . Ntalker + A program that implements a multicast "chat" program. + + + . Proactor + A program that illustrates the "Proactive" version of + the Reactor diff --git a/ACE/examples/Reactor/TP_Reactor/AcceptHandler.cpp b/ACE/examples/Reactor/TP_Reactor/AcceptHandler.cpp new file mode 100644 index 00000000000..7ae34d2b2b4 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/AcceptHandler.cpp @@ -0,0 +1,106 @@ +/* + * $Id$ + * + * ACE reactor demonstration + * + * Date: 26-Jan-2006 + */ + +#include <ace/Auto_Ptr.h> +#include <ace/INET_Addr.h> + +#include "common.h" +#include "AcceptHandler.h" +#include "ReadHandler.h" + +AcceptHandler:: AcceptHandler(ACE_Reactor *reactor) : + ACE_Event_Handler(), + mReactor(reactor == 0 ? ACE_Reactor::instance() : reactor), + mAcceptor() { + ACE_TRACE("AcceptHandler:: AcceptHandler(ACE_Reactor *)"); +} + +AcceptHandler::~AcceptHandler() { + ACE_TRACE("AcceptHandler::~AcceptHandler()"); +} + +int AcceptHandler::open(void) { + ACE_TRACE("AcceptHandler::open(void)"); + + // create the local address used for the service (PORT is from common.h) + ACE_INET_Addr addr(PORT); + + // open a port using the acceptor; reuse the address later + if (mAcceptor.open(addr, 1) == -1) + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to open ") + ACE_TEXT ("listening socket. (errno = %i: %m)\n"), errno), -1); + + // register the handler with the reactor + if (mReactor->register_handler(this, + ACE_Event_Handler::ACCEPT_MASK) == -1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to register accept ") + ACE_TEXT ("handler. (errno = %i: %m)\n"), errno)); + + // don't leave the acceptor open + if (mAcceptor.close() == -1) + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close the socket ") + ACE_TEXT ("after previous error. (errno = %i: %m)\n"), + errno)); + return -1; + } + + return 0; +} + +ACE_HANDLE AcceptHandler::get_handle(void) const { + ACE_TRACE("AcceptHandler::get_handle(void)"); + return mAcceptor.get_handle(); +} + +int AcceptHandler::handle_input(ACE_HANDLE) { + ACE_TRACE("AcceptHandler::handle_input(ACE_HANDLE)"); + + ACE_INET_Addr clientAddr; + + // create a new ReadHandler + ReadHandler *reader = 0; + ACE_NEW_NORETURN (reader, ReadHandler()); + if (reader == 0) + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to allocate ") + ACE_TEXT ("reader. (errno = %i: %m)\n"), errno), -1); + + // put reader in an auto pointer so we can use ACE_ERROR_RETURN safely + auto_ptr<ReadHandler> pReader(reader); + + // accept the connection using the reader's stream + if (mAcceptor.accept(reader->getStream(), &clientAddr) == -1) + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to accept ") + ACE_TEXT ("client connection. (errno = %i: %m)\n"), errno), -1); + + // register the reader with the reactor + if (mReactor->register_handler(reader, + ACE_Event_Handler::READ_MASK) == -1) + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to register ") + ACE_TEXT ("read handler. (errno = %i: %m)\n"), errno), -1); + + // from now on the read handler takes care of itself + pReader.release(); + + return 0; // keep going +} + +int AcceptHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask) { + ACE_TRACE("AcceptHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask)"); + + // close the listening socket + if (mAcceptor.close() == -1) + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close the ") + ACE_TEXT ("socket. (errno = %i: %m)\n"), errno)); + + // no need to distinguish between error during close and normal close + // since ACE does not evaluate the return value of handle_close() + + delete this; + return 0; +} + diff --git a/ACE/examples/Reactor/TP_Reactor/AcceptHandler.h b/ACE/examples/Reactor/TP_Reactor/AcceptHandler.h new file mode 100644 index 00000000000..036f7a36f5a --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/AcceptHandler.h @@ -0,0 +1,75 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#ifndef __ACCEPTHANDLER_H__ +#define __ACCEPTHANDLER_H__ + +#include <ace/Event_Handler.h> +#include <ace/Reactor.h> +#include <ace/SOCK_Acceptor.h> + +/** + * This accept handler is based on the provided solution from the ACE course. + */ +class AcceptHandler : public ACE_Event_Handler { + + private: + + /** + * The reactor to which the accept handler belongs. + */ + ACE_Reactor *mReactor; + + /** + * The socket used for incoming conections. + */ + ACE_SOCK_Acceptor mAcceptor; + + public: + + /** + * @param reactor The reactor which will use this accept handler. + */ + AcceptHandler(ACE_Reactor *reactor = 0); + + /** + * The destructor exists for tracing purposes. + */ + virtual ~AcceptHandler(); + + /** + * Open the listening socket and register the handler with the reactor. + * + * @return 0 on success, -1 on failure + */ + int open(void); + + /** + * @name Overridden methods from the ACE_Event_Handler + */ + // @{ + + /** + * Provides the handle of mAcceptor. + */ + virtual ACE_HANDLE get_handle(void) const; + + /** + * Create a read handler for the new connection and register that + * handler with the reactor. + */ + virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); + + /** + * Close the listening socket. + */ + virtual int handle_close(ACE_HANDLE, ACE_Reactor_Mask); + // @} +}; + +#endif /* __ACCEPTHANDLER_H__ */ + diff --git a/ACE/examples/Reactor/TP_Reactor/Makefile.am b/ACE/examples/Reactor/TP_Reactor/Makefile.am new file mode 100644 index 00000000000..5407cbb10e1 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/Makefile.am @@ -0,0 +1,53 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +## Makefile.TP_Reactor_Client.am +noinst_PROGRAMS = client + +client_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +client_SOURCES = \ + client.cpp \ + AcceptHandler.h \ + ReadHandler.h \ + common.h + +client_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Makefile.TP_Reactor_Server.am +noinst_PROGRAMS += server + +server_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +server_SOURCES = \ + AcceptHandler.cpp \ + ReadHandler.cpp \ + server.cpp \ + AcceptHandler.h \ + ReadHandler.h + +server_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/TP_Reactor/README b/ACE/examples/Reactor/TP_Reactor/README new file mode 100644 index 00000000000..32fbc15aca9 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/README @@ -0,0 +1,86 @@ +ACE reactor demonstration +========================= + +Martin Kolleck +Tino Riethmuller + + + +1. Introduction + +This program demonstrates what we think is a bug in the ACE library. The +affected component is the ACE_TP_Reactor. According to the documentation, the +reactor ensures that only one of the handle_*() methods of an event handler is +called at a time. Tino found this to be not true and I wrote this example +program showing the behavior. I do not exclude the possibility that we are +using the ACE library in an unintended/wrong way. So comments on the code as +well as any other remarks are welcome. + + + +2. The program + +The program consists of a client and a server. The general implementation is +taken from the example solution to exercise 4c of the ACE course. The client +will send a request to the server. This request is interpreted to be the size +of the following data. The server allocates the memory required to hold the +client's data and then sends a confirmation to the client, that it may +proceed. The the client sends the large data chunk and the server again +confirms it. + +The client runs in a loop which can be configured to run indefinitely or a +previously set amount of times. The configuration i done from the command +line. To invoke the client type: + + $ ./client size [count] + +<size> sets the size (in MiB) of the buffer sent to the server. Depending on +the systems, values between 60 and 100 have been used for testing. <count> +determines how often the buffer is sent. If left out, the clients send the +buffer until interrupted. + +The server is started without arguments. Both programs will print a dot for +each successful connection. I found this an easy and unintrusive way of showing +progress whithout flooding the console too fast. This also makes it easier to +see when an error has occurred. + + + +3. Building the program + +This example was created on a Linux box. You will need the environment +variable ACE_ROOT set up to the location where ACE is installed. It might be +possible, that the path where the ACE libraries are found, needs to be adjusted +in the Makefile. + +To compile simply type 'make' on the command prompt. + + $ make + +This will create two executable files. One for the server and one for the +client. (named respectively) + + + +4. Running the program + +The error seems to be of statistical nature. Occurring only under certain +conditions (which I am not sure of, what they are). I successfully produced +the error on the four machines given below (architecture, ACE and compiler +version). I tested the program with localhost connections, as well as over +a real network connection and could always reproduce the error. + +To detect the error I introduced a member variable to the read event handler. +This counter is initialized to zero in the constructor. When handle_input() of +the event handler is called, the counter is increased and decreased, when +handle_input() returns. Before increasing the counter, It is compared to zero +(which it should alway be, if only one invocation to handle_input() is made +at a time) and an error message is printed if it is not zero. + +To test for the error, I ran one instance of the server program and TWO +instances of the client program. The sizes of the buffers were between 60 and +100 MiB and no count was given (running until stopped) The three Linux boxes +showed the error within one minute of starting both clients. For the Windows +box I decreased the buffer size to 15 and 20 MiB (Windows does not seem to have +very performant localhost connectivity) and it took about half an +hour until the error occurred the first time. diff --git a/ACE/examples/Reactor/TP_Reactor/ReadHandler.cpp b/ACE/examples/Reactor/TP_Reactor/ReadHandler.cpp new file mode 100644 index 00000000000..06c6c953046 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/ReadHandler.cpp @@ -0,0 +1,151 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#include <ace/streams.h> +#include <ace/Time_Value.h> + +#include "common.h" +#include "ReadHandler.h" + +/** + * This macro is used to increase the invocation counter by one when entering + * handle_input(). It also checks wether the counter is greater than zero + * indicating, that handle_input() has been called before. + */ +#define INVOCATION_ENTER() do { if (mInvocationCounter > 0) \ + ACE_ERROR((LM_ERROR, ACE_TEXT("Multiple invocations detected.\n"))); \ + mInvocationCounter++; } while (0) + +/** + * THis macro is the counter part to INVOCATION_ENTER(). It decreases the + * invocation counter and then returns the given value. This macro is + * here for convenience to decrease the invocation counter also when returning + * due to errors. + */ +#define INVOCATION_RETURN(retval) do { mInvocationCounter--; \ + return retval; } while(0) + +ReadHandler::ReadHandler() : ACE_Event_Handler(), mStream(), mDataSize(0), + mData(0), mCallCounter(0), mInvocationCounter(0) { + ACE_TRACE(ACE_TEXT("ReadHandler::ReadHandler()")); +} + +ReadHandler::~ReadHandler() { + ACE_TRACE(ACE_TEXT("ReadHandler::~ReadHandler()")); + + if (mStream.close() == -1) + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close socket. ") + ACE_TEXT ("(errno = %i: %m)\n"), errno)); + + delete[] mData; +} + +ACE_SOCK_Stream &ReadHandler::getStream(void) { + ACE_TRACE(ACE_TEXT("ReadHandler::getStream(void)")); + return mStream; +} + +ACE_HANDLE ReadHandler::get_handle(void) const { + ACE_TRACE(ACE_TEXT("ReadHandler::get_handle(void)")); + return mStream.get_handle(); +} + +int ReadHandler::handle_input(ACE_HANDLE) { + ACE_TRACE(ACE_TEXT("ReadHandler::handle_input(ACE_HANDLE)")); + + INVOCATION_ENTER(); + + // the response sent to the client + char response = 0; + + if (mCallCounter == 0) { + + /* + * This is the first request from the client. + */ + + // increase the call counter so the next client request goes to else-if + mCallCounter++; + + // get the desired size from the client + // Note: only use the sizeof and pointer to int on compatible + // platforms (i.e. little-endian/big-endian, data type size) + if (mStream.recv_n(&mDataSize, sizeof(mDataSize), + &connTimeout) != sizeof(mDataSize)) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to receive ") + ACE_TEXT ("request. (errno = %i: %m)\n"), errno)); + INVOCATION_RETURN(-1); + } + + // The verbose debug output is replaced with some unintrusive dots. + // This increases visibility of the desired effect. + // ACE_DEBUG((LM_DEBUG, ACE_TEXT("%@: Data size: %i\n"), this, mDataSize)); + ACE_DEBUG((LM_DEBUG, ACE_TEXT("."))); + + // check mDataSize for plausability then allocate memory + if (mDataSize > 0) { + mData = new (std::nothrow) char[mDataSize]; + if (mData == 0) + ACE_DEBUG((LM_DEBUG, ACE_TEXT("%N:%l: Failed to allocate ") + ACE_TEXT ("data buffer.\n"))); + else + response = 'K'; + } + + // send the response to the client (which is still 0, if the + // allocation did not succeed) + if (mStream.send_n(&response, sizeof(response), &connTimeout) != 1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ") + ACE_TEXT ("response. (errno = %i: %m)\n"), errno)); + INVOCATION_RETURN(-1); + } + + if (response == 'K') + INVOCATION_RETURN(0); // get another request from the same client + else + INVOCATION_RETURN(-1); // the client will not send data if response != 'K' + + } else if (mCallCounter == 1) { + + /* + * This is the second request from the client. + */ + + // increase the call counter, this read handler should not be called + // again + mCallCounter++; + + // receive the data from the client + if (mStream.recv_n(mData, mDataSize, &connTimeout) != mDataSize) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to receive data.") + ACE_TEXT ("(errno = %i: %m)\n"), errno)); + INVOCATION_RETURN(-1); + } + + response = 'K'; + + if (mStream.send_n(&response, 1, &connTimeout) != 1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ") + ACE_TEXT ("confirmation. (errno = %i: %m)\n"), errno)); + INVOCATION_RETURN(-1); + } + + INVOCATION_RETURN(-1); // ask for removal, since client does not send any more data + } + + // this is to find strange actions with the call counter + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: We should not get here."))); + INVOCATION_RETURN(-1); +} + +int ReadHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask) { + ACE_TRACE("ReadHandler::handle_close(ACE_HANDLE, ACE_Reactor_Mask)"); + + delete this; + return 0; +} + diff --git a/ACE/examples/Reactor/TP_Reactor/ReadHandler.h b/ACE/examples/Reactor/TP_Reactor/ReadHandler.h new file mode 100644 index 00000000000..41d58b6008a --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/ReadHandler.h @@ -0,0 +1,92 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#ifndef __READHANDLER_H__ +#define __READHANDLER_H__ + +#include <ace/Event_Handler.h> +#include <ace/SOCK_Stream.h> + +/** + * This read handler is created by the accept handler and handles all the data + * exchange between client and server. The client makes two requests to the + * server. The first asks the server to create a buffer which will hold the + * data sent in the second call. + */ +class ReadHandler : public ACE_Event_Handler { + + private: + + /** + * The stream socket used for data exchange. + */ + ACE_SOCK_Stream mStream; + + /** + * The size of the data array. + */ + int mDataSize; + + /** + * The array containing the client's data. + */ + char *mData; + + /** + * The call counter to distinguish between first and second call. + */ + int mCallCounter; + + /** + * Count the numer of invocations of handle_*(). According to the + * docs, there should be only one invocation at any given time. + */ + int mInvocationCounter; + + public: + + /** + * Initialization. + */ + ReadHandler(void); + + /** + * Clean up data. + */ + virtual ~ReadHandler(); + + /** + * Provide access to the internal stream socket. + */ + ACE_SOCK_Stream &getStream(void); + + /** + * @name Overridden methods from the ACE_Event_Handler + */ + // @{ + + /** + * Provides the handle of mStream; + */ + virtual ACE_HANDLE get_handle(void) const; + + /** + * Handles the data excahnge between client and server. On the first + * invocation, mData is allocated to the requested size and on the + * second invocation, that buffer is filled with the client's data. + */ + virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); + + /** + * Deletes this instance of the read handler. + */ + virtual int handle_close(ACE_HANDLE, ACE_Reactor_Mask); + // @} +}; + +#endif /* __READHANDLER_H__ */ + diff --git a/ACE/examples/Reactor/TP_Reactor/TP_Reactor.mpc b/ACE/examples/Reactor/TP_Reactor/TP_Reactor.mpc new file mode 100644 index 00000000000..03d8de2e7aa --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/TP_Reactor.mpc @@ -0,0 +1,18 @@ +// -*- MPC -*- +// $Id$ + +project (*client) : aceexe { + exename = client + Source_Files { + client.cpp + } +} + +project (*server) : aceexe { + exename = server + Source_Files { + server.cpp + AcceptHandler.cpp + ReadHandler.cpp + } +} diff --git a/ACE/examples/Reactor/TP_Reactor/client.cpp b/ACE/examples/Reactor/TP_Reactor/client.cpp new file mode 100644 index 00000000000..509f2e9c457 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/client.cpp @@ -0,0 +1,141 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#include <ace/Auto_Ptr.h> +#include <ace/INET_Addr.h> +#include <ace/Log_Msg.h> +#include <ace/OS.h> +#include <ace/SOCK_Acceptor.h> +#include <ace/SOCK_Connector.h> +#include <ace/SOCK_Stream.h> +#include <ace/streams.h> + +#include "common.h" + +/** + * Print usage information for the client. + * + * @param arg The progams name (argv[0]). + */ +int printUsage(ACE_TCHAR *arg) { + cerr << "Usage: " << arg << " size [count]" << endl; + cerr << "\tSends <size> MiB to the server and optionally repeats that " + << "<count> times." << endl; + cerr << "\tAll arguments must be positive numbers. If no <count> is " + << "given, the\n\tclient runs until interrupted." << endl; + return -1; +} + +int ACE_TMAIN(int argc, ACE_TCHAR **argv) { + + // size and count for transmissions + int size = 0, count = -1; + + // the server's answer is a single byte + char answer; + + // parse the <size> argument + if ((argc < 2) || (((size = ACE_OS::strtol(argv[1], 0, 10)) < 1) || + (errno == EINVAL))) + return printUsage(argv[0]); + + // take size as the number of MiB and create appropriate buffer + size *= BASE; + char *someData = new (std::nothrow) char[size]; + + if (someData == 0) + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%N:%l: Failed to allocate ") + ACE_TEXT ("data buffer.\n")), -1); + + // put someData in an auto_ptr so it gets deleted automatically + auto_ptr<char> pSomeData(someData); + + // parse the <count> argument if available + if ((argc == 3) && (((count = ACE_OS::strtol(argv[2], 0, 10)) < 1) || + (errno == EINVAL))) + return printUsage(argv[0]); + + // the server listens on localhost on default port (from common.h) + ACE_INET_Addr serverAddr(PORT, "localhost"); + + ACE_SOCK_Stream stream; + ACE_SOCK_Connector connector; + + // -1 is running indefinitely + while ((count == -1) || (count-- != 0)) { + + // some output, that we know something is happening + //ACE_DEBUG((LM_DEBUG, ACE_TEXT("%N:%l: Passes left: %i\n"), count)); + ACE_DEBUG((LM_DEBUG, ACE_TEXT("."))); + + // connect to the server and get the stream + if (connector.connect(stream, serverAddr) == -1) { + ACE_ERROR((LM_ERROR, + ACE_TEXT("%N:%l: Failed to connect to ") + ACE_TEXT ("server. (errno = %i: %m)\n"), errno)); + break; + } + + try { + + // send the request to the server (number of MiB in the next call) + // Note: only use the sizeof and pointer to int on compatible + // platforms (i.e. little-endian/big-endian, data type size) + if (stream.send_n(&size, sizeof(size), &connTimeout) != sizeof(size)) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ") + ACE_TEXT ("request. (errno = %i: %m)\n"), errno)); + throw(1); + } + + // receive the answer + if (stream.recv_n(&answer, sizeof(answer), &connTimeout) != 1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N: %l: Failed to receive ") + ACE_TEXT ("1st response. (errno = %i: %m)\n"), errno)); + throw(1); + } + + // server answer, 'K" indicates a positive answer + if (answer == 'K') { + + // send a huge message to the server + if (stream.send_n(someData, size, &connTimeout) != size) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to send ") + ACE_TEXT ("someData. (errno = %i: %m)\n"), errno)); + throw(1); + } + + // get an answer + if (stream.recv_n(&answer, sizeof(answer), &connTimeout) != 1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N: %l: Failed to receive ") + ACE_TEXT ("2nd response. (errno = %i: %m)\n"), errno)); + throw(1); + } + + // check the answer + if (answer != 'K') { + cout << "The server was unable to process the data." + << endl; + } + } + } catch (...) { + // ok we know an error occurred, we need to close the socket. + // The we'll try again. + } + + // close the current stream + if (stream.close() == -1) { + ACE_ERROR((LM_ERROR, ACE_TEXT("%N:%l: Failed to close ") + ACE_TEXT ("socket. (errno = %i: %m)\n"), errno)); + break; + } + } // while + + cout << "Bye. Bye" << endl; + return 0; +} + diff --git a/ACE/examples/Reactor/TP_Reactor/common.h b/ACE/examples/Reactor/TP_Reactor/common.h new file mode 100644 index 00000000000..c9661027923 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/common.h @@ -0,0 +1,29 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +#include <ace/Time_Value.h> + +/** + * The port number used by client and server. + */ +static const int PORT = 4711; + +/** + * The base size. 0x100000 = 1 MiB + */ +static const int BASE = 0x100000; + +/** + * The timeout value for connections. (30 seconds) + */ +static const ACE_Time_Value connTimeout(30); + +#endif /* __COMMON_H__ */ + diff --git a/ACE/examples/Reactor/TP_Reactor/run_test.pl b/ACE/examples/Reactor/TP_Reactor/run_test.pl new file mode 100644 index 00000000000..ac07295a735 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/run_test.pl @@ -0,0 +1,41 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- + +use lib '../../../bin'; +use PerlACE::Run_Test; + +$status = 0; + +$SV = new PerlACE::Process ("server", ""); +$CL1 = new PerlACE::Process ("client", "80 100"); +$CL2 = new PerlACE::Process ("client", "80 100"); +$SV->Spawn (); + +sleep (1); + +$client1 = $CL1->Spawn (); + +if ($client1 != 0) { + print STDERR "ERROR: client 1 returned $client1\n"; + $status = 1; +} + +$client2 = $CL2->Spawn (); + +if ($client2 != 0) { + print STDERR "ERROR: client 2 returned $client2\n"; + $status = 1; +} + +$server = $SV->WaitKill (1000); + +if ($server != 0) { + print STDERR "ERROR: server returned $server\n"; + $status = 1; +} + +exit $status; diff --git a/ACE/examples/Reactor/TP_Reactor/server.cpp b/ACE/examples/Reactor/TP_Reactor/server.cpp new file mode 100644 index 00000000000..0c147818424 --- /dev/null +++ b/ACE/examples/Reactor/TP_Reactor/server.cpp @@ -0,0 +1,66 @@ +/* + * ACE reactor demonstration + * + * $Id$ + * Date: 26-Jan-2006 + */ + +#include <ace/Event_Handler.h> +#include <ace/Log_Msg.h> +#include <ace/OS.h> +#include <ace/Reactor.h> +#include <ace/Signal.h> +#include <ace/streams.h> +#include <ace/Thread_Manager.h> +#include <ace/TP_Reactor.h> + +#include "AcceptHandler.h" + +/** + * This is the function run by all threads in the thread pool. + * + * @param arg is expected to be of type (ACE_Reactor *) + */ +ACE_THR_FUNC_RETURN threadFunc(void *arg) { + ACE_TRACE("threadFunc(void *)"); + + ACE_Reactor *reactor = (ACE_Reactor *) arg; + reactor->run_reactor_event_loop(); + + return 0; +} + +/** + * The main function sets up the TP reactor. The code is basically taken from + * the solution to exercise 4c of the ACE course. + */ +int ACE_TMAIN(int, ACE_TCHAR **) { + + // create a reactor from a TP reactor + ACE_TP_Reactor tpReactor; + ACE_Reactor reactor(&tpReactor); + + // create a new accept handler using that reactor + AcceptHandler *acceptHandler = 0; + ACE_NEW_NORETURN (acceptHandler, AcceptHandler(&reactor)); + if (acceptHandler == 0) + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to allocate ") + ACE_TEXT ("accept handler. (errno = %i: %m)\n"), errno), -1); + + // open the accept handler + if (acceptHandler->open() == -1) { + delete acceptHandler; + ACE_ERROR_RETURN((LM_ERROR, ACE_TEXT("%N:%l: Failed to open accept ") + ACE_TEXT ("handler. Exiting.\n")), -1); + } + + // spawn some threads which run the reactor event loop(s) + ACE_Thread_Manager::instance()->spawn_n(9, threadFunc, &reactor); + + // let the thread manager wait for all threads + ACE_Thread_Manager::instance()->wait(); + + ACE_DEBUG((LM_DEBUG, ACE_TEXT("Bye. Bye.\n"))); + return 0; +} + diff --git a/ACE/examples/Reactor/WFMO_Reactor/APC.cpp b/ACE/examples/Reactor/WFMO_Reactor/APC.cpp new file mode 100644 index 00000000000..bf42fd1edfa --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/APC.cpp @@ -0,0 +1,124 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// APC.cpp +// +// = DESCRIPTION +// +// Tests the WFMO_Reactor's ability to handle regular APC +// notifications. +// +// = AUTHOR +// +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Auto_Event.h" + +ACE_RCSID(WFMO_Reactor, APC, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + int handle_signal (int signum, + siginfo_t * = 0, + ucontext_t * = 0); + + int handle_timeout (const ACE_Time_Value &tv, + const void *arg = 0); + + ACE_Auto_Event handle_; + int iterations_; +}; + +static Event_Handler *global_event_handler; + +static void WINAPI +apc_callback (DWORD) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) apc occured @ %T\n")); + + global_event_handler->handle_.signal (); +} + +void +queue_apc (void) +{ + DWORD result = ::QueueUserAPC (reinterpret_cast<PAPCFUNC> (&apc_callback), + // pointer to APC function + ::GetCurrentThread (), // handle to the thread + 0); // argument for the APC function + if (result == FALSE) + ACE_OS::exit (-1); +} + +int +Event_Handler::handle_signal (int, + siginfo_t *, + ucontext_t *) +{ + --this->iterations_; + + if (this->iterations_ == 0) + { + ACE_Reactor::instance ()->remove_handler (this->handle_.handle (), + ACE_Event_Handler::DONT_CALL); + ACE_Reactor::end_event_loop (); + } + + return 0; +} + +int +Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + ACE_DEBUG ((LM_DEBUG, + "(%t) timeout occured @ %T\n")); + queue_apc (); + return 0; +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Event_Handler event_handler; + event_handler.iterations_ = 5; + global_event_handler = &event_handler; + + int result = ACE_Reactor::instance ()->register_handler (&event_handler, + event_handler.handle_.handle ()); + ACE_ASSERT (result == 0); + + ACE_Time_Value timeout (2); + result = ACE_Reactor::instance ()->schedule_timer (&event_handler, + 0, + timeout, + timeout); + ACE_ASSERT (result != -1); + + ACE_Reactor::run_alertable_event_loop (); + + ACE_Reactor::instance ()->cancel_timer(&event_handler); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Abandoned.cpp b/ACE/examples/Reactor/WFMO_Reactor/Abandoned.cpp new file mode 100644 index 00000000000..c860fc2810c --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Abandoned.cpp @@ -0,0 +1,141 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Abandoned.cpp +// +// = DESCRIPTION +// +// Tests the WFMO_Reactor's ability to handle abandoned mutexes. +// +// = AUTHOR +// +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Thread_Manager.h" +#include "ace/Process_Mutex.h" +#include "ace/Auto_Event.h" + +ACE_RCSID(WFMO_Reactor, Abandoned, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + int handle_signal (int signum, + siginfo_t * = 0, + ucontext_t * = 0); + + int handle_timeout (const ACE_Time_Value &tv, + const void *arg = 0); + + ACE_Auto_Event handle_; + ACE_Process_Mutex *mutex_; + int iterations_; +}; + +static int abandon = 1; + +static ACE_THR_FUNC_RETURN +worker (void *data) +{ + Event_Handler *handler = (Event_Handler *) data; + + handler->handle_.signal (); + handler->mutex_->acquire (); + + if (!abandon) + handler->mutex_->release (); + + return 0; +} + +int +Event_Handler::handle_signal (int, + siginfo_t *s, + ucontext_t *) +{ + ACE_HANDLE handle = s->si_handle_; + if (handle == this->handle_.handle ()) + ACE_Reactor::instance ()->register_handler (this, + this->mutex_->lock ().proc_mutex_); + else + { + ACE_Reactor::instance ()->remove_handler (this->mutex_->lock ().proc_mutex_, + ACE_Event_Handler::DONT_CALL); + delete this->mutex_; + } + + return 0; +} + +int +Event_Handler::handle_timeout (const ACE_Time_Value &, + const void *) +{ + --this->iterations_; + ACE_DEBUG ((LM_DEBUG, + "(%t) timeout occured @ %T, iterations left %d\n", + this->iterations_)); + + if (this->iterations_ == 0) + { + ACE_Reactor::instance ()->remove_handler (this->handle_.handle (), + ACE_Event_Handler::DONT_CALL); + + ACE_Reactor::instance ()->cancel_timer (this); + + ACE_Reactor::end_event_loop (); + } + else + { + ACE_NEW_RETURN (this->mutex_, + ACE_Process_Mutex, + -1); + int result = ACE_Thread_Manager::instance ()->spawn (&worker, this); + ACE_ASSERT (result != -1); + ACE_UNUSED_ARG (result); + } + + return 0; +} + +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + Event_Handler event_handler; + + event_handler.iterations_ = 5; + int result = ACE_Reactor::instance ()->register_handler + (&event_handler, + event_handler.handle_.handle ()); + ACE_ASSERT (result == 0); + + ACE_Time_Value timeout (2); + result = ACE_Reactor::instance ()->schedule_timer (&event_handler, + 0, + timeout, + timeout); + ACE_ASSERT (result != -1); + + ACE_Reactor::run_event_loop (); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Console_Input.cpp b/ACE/examples/Reactor/WFMO_Reactor/Console_Input.cpp new file mode 100644 index 00000000000..363a6a8ead8 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Console_Input.cpp @@ -0,0 +1,87 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Console_Input.cpp +// +// = DESCRIPTION +// +// This application tests the working of WFMO_Reactor when users +// are interested in console input. +// +// = AUTHOR +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/Reactor.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_string.h" +#include "ace/OS_main.h" + +ACE_RCSID(WFMO_Reactor, Console_Input, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler (ACE_Reactor &reactor); + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); +}; + +Event_Handler::Event_Handler (ACE_Reactor &reactor) +{ + this->reactor (&reactor); + + if (this->reactor ()->register_handler (this, + ACE_STDIN) != 0) + ACE_ERROR ((LM_ERROR, + "Registration with Reactor could not be done\n")); +} + +int +Event_Handler::handle_signal (int, siginfo_t *, ucontext_t *) +{ + ACE_TCHAR buffer[BUFSIZ]; + int result = ACE_OS::read (ACE_STDIN, buffer, sizeof buffer); + buffer[result] = '\0'; + + if (result <= 0) + { + this->reactor ()->close (); + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "ACE_OS::read"), -1); + } + + if (ACE_OS::strcmp (ACE_TEXT("quit\r\n"), buffer) == 0) + this->reactor ()->close (); + + ACE_DEBUG ((LM_DEBUG, "User input: %s", buffer)); + + return 0; +} + +int +Event_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Event_Handler removed from Reactor\n")); + return 0; +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_Reactor reactor; + Event_Handler handler (reactor); + + int result = 0; + while (result != -1) + result = reactor.handle_events (); + + return 0; +} diff --git a/ACE/examples/Reactor/WFMO_Reactor/Directory_Changes.cpp b/ACE/examples/Reactor/WFMO_Reactor/Directory_Changes.cpp new file mode 100644 index 00000000000..7f5126c6cba --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Directory_Changes.cpp @@ -0,0 +1,128 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Directory_Changes.cpp +// +// = DESCRIPTION +// +// This application tests the working of WFMO_Reactor when users +// are interested in monitoring changes in the filesystem. +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/OS_NS_unistd.h" +#include "ace/OS_NS_fcntl.h" + +ACE_RCSID(WFMO_Reactor, Directory_Changes, "$Id$") + +static int stop_test = 0; +static const ACE_TCHAR *directory = ACE_TEXT ("."); +static const ACE_TCHAR *temp_file = ACE_TEXT ("foo"); + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler (ACE_Reactor &reactor); + ~Event_Handler (void); + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + +private: + ACE_HANDLE handle_; +}; + +Event_Handler::Event_Handler (ACE_Reactor &reactor) + : handle_ (ACE_INVALID_HANDLE) +{ + this->reactor (&reactor); + + int change_notification_flags = FILE_NOTIFY_CHANGE_FILE_NAME; + + this->handle_ = ACE_TEXT_FindFirstChangeNotification (directory, // pointer to name of directory to watch + FALSE, // flag for monitoring directory or directory tree + change_notification_flags // filter conditions to watch for + ); + if (this->handle_ == ACE_INVALID_HANDLE) + ACE_ERROR ((LM_ERROR, "FindFirstChangeNotification could not be setup\n")); + + if (this->reactor ()->register_handler (this, + this->handle_) != 0) + ACE_ERROR ((LM_ERROR, "Registration with Reactor could not be done\n")); +} + +Event_Handler::~Event_Handler (void) +{ +} + +int +Event_Handler::handle_signal (int, siginfo_t *, ucontext_t *) +{ + ::FindNextChangeNotification (this->handle_); + if (stop_test) + this->reactor ()->close (); + return 0; +} + +int +Event_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Event_Handler removed from Reactor\n")); + ::FindCloseChangeNotification (this->handle_); + return 0; +} + +void +worker (void) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Thread creation\n")); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread creating temporary file\n")); + ACE_HANDLE file = ACE_OS::open (temp_file, _O_CREAT | _O_EXCL); + if (file == ACE_INVALID_HANDLE) + ACE_ERROR ((LM_ERROR, "Error in creating %s: %p\n", temp_file, "ACE_OS::open")); + else + { + ACE_OS::close (file); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (3); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread removing temporary file\n")); + stop_test = 1; + ACE_OS::unlink (temp_file); + } +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_Reactor reactor; + Event_Handler handler (reactor); + + int result = ACE_OS::thr_create ((ACE_THR_FUNC) worker, 0, 0, 0); + ACE_ASSERT (result == 0); + + for (result = 0; result != -1; result = reactor.handle_events ()) + continue; + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Exceptions.cpp b/ACE/examples/Reactor/WFMO_Reactor/Exceptions.cpp new file mode 100644 index 00000000000..5bbf0b4e4f3 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Exceptions.cpp @@ -0,0 +1,109 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Exceptions.cpp +// +// = DESCRIPTION +// +// This test application tests the state of WFMO_Reactor when +// exceptions occurs when executing user callbacks. +// +// The thread count in WFMO_Reactor is used to ensure that state of +// WFMO_Reactor is not fouled up when exceptions occur in user code. +// This example also shows how to write event loops that survive +// user exceptions +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/WFMO_Reactor.h" + +ACE_RCSID(WFMO_Reactor, Exceptions, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler (void) + : event_ (1) + { + ACE_DEBUG ((LM_DEBUG, + "Event_Handler created\n")); + } + + ~Event_Handler (void) + { + ACE_DEBUG ((LM_DEBUG, + "Event_Handler destroyed\n")); + } + + int handle_signal (int, siginfo_t * = 0, ucontext_t * = 0) + { + char *cause_exception = 0; + char a = *cause_exception; + ACE_UNUSED_ARG(a); + return 0; + } + + ACE_HANDLE get_handle (void) const + { + return this->event_.handle (); + } +private: + ACE_Manual_Event event_; +}; + +class ACE_WFMO_Reactor_Test +{ +public: + static void doit (ACE_WFMO_Reactor &wfmo_reactor) + { + for (int i = 1; i <= 10; i++) + { + ACE_DEBUG ((LM_DEBUG, + "Active threads in WFMO_Reactor (before handle_events) = %d\n", + wfmo_reactor.active_threads_)); + ACE_SEH_TRY + { + wfmo_reactor.handle_events (); + } + ACE_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) + { + ACE_DEBUG ((LM_DEBUG, + "Exception occurred\n")); + } + ACE_DEBUG ((LM_DEBUG, + "Active threads in WFMO_Reactor (after handle_events) = %d\n", + wfmo_reactor.active_threads_)); + } + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Event_Handler handler; + ACE_WFMO_Reactor wfmo_reactor; + wfmo_reactor.register_handler (&handler); + + ACE_WFMO_Reactor_Test::doit (wfmo_reactor); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Handle_Close.cpp b/ACE/examples/Reactor/WFMO_Reactor/Handle_Close.cpp new file mode 100644 index 00000000000..9eb6d7c727a --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Handle_Close.cpp @@ -0,0 +1,333 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Handle_Close.cpp +// +// = DESCRIPTION +// +// This application tests whether handle_close gets called and if +// the correct masks are passed along. The handler should get +// handle_close called for all three masks (READ, WRITE, and +// EXCEPT). +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/Get_Opt.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Select_Reactor.h" +#include "ace/Auto_Ptr.h" +#include "ace/Pipe.h" +#include "ace/OS_main.h" + +ACE_RCSID(WFMO_Reactor, Handle_Close, "$Id: ") + +// Use the WFMO_Reactor +static int opt_wfmo_reactor = 0; + +// Use the Select_Reactor +static int opt_select_reactor = 0; + +// Make pipe readable in main() +static int write_to_pipe_in_main = 0; + +// Cancel reads +static int cancel_reads = 0; + +// Write some data to the pipe. This will cause handle_input to get +// called. +void +write_to_pipe (ACE_Pipe &pipe) +{ + char *data = "hello"; + size_t len = ACE_OS::strlen (data); + + ssize_t result = ACE::send (pipe.write_handle (), + data, + len); + ACE_ASSERT ((size_t)result == len); + ACE_UNUSED_ARG (result); +} + +// Simple handler class +class Handler : public ACE_Event_Handler +{ +public: + Handler (ACE_Pipe &pipe) + : pipe_ (pipe) + { + } + + ~Handler (void) + { + this->reactor (0); + } + + ACE_HANDLE get_handle (void) const + { + return this->pipe_.read_handle (); + } + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, + "Handler::handle_close called with mask = %d\n", + close_mask)); + return 0; + } + + int handle_input (ACE_HANDLE handle) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, "Handler::handle_input\n")); + + // Remove for reading + return -1; + } + + int handle_output (ACE_HANDLE handle) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, "Handler::handle_output\n")); + + // Optionally cancel reads + if (cancel_reads) + { + int result = this->reactor ()->cancel_wakeup (this, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result != -1); + ACE_UNUSED_ARG (result); + } + + // Write to the pipe; this causes handle_input to get called. + if (!write_to_pipe_in_main) + write_to_pipe (this->pipe_); + + // Remove for writing + return -1; + } + +protected: + ACE_Pipe &pipe_; +}; + +class Different_Handler : public ACE_Event_Handler +{ +public: + + Different_Handler (ACE_Pipe &pipe) + : pipe_ (pipe) + { + } + + ~Different_Handler (void) + { + this->reactor (0); + } + + ACE_HANDLE get_handle (void) const + { + return this->pipe_.read_handle (); + } + + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, + "Different_Handler::handle_close called with mask = %d\n", + close_mask)); + return 0; + } + + int handle_input (ACE_HANDLE handle) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, "Different_Handler::handle_input\n")); + + // Remove for reading + int result = this->reactor ()->remove_handler (this, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + return 0; + } + + int handle_output (ACE_HANDLE handle) + { + ACE_UNUSED_ARG (handle); + ACE_DEBUG ((LM_DEBUG, "Different_Handler::handle_output\n")); + + // Add for reading + int result = this->reactor ()->mask_ops (this, + ACE_Event_Handler::READ_MASK, + ACE_Reactor::ADD_MASK); + ACE_ASSERT (result != -1); + + ACE_Reactor_Mask old_masks = + ACE_Event_Handler::WRITE_MASK | + ACE_Event_Handler::EXCEPT_MASK; + + ACE_ASSERT (old_masks == + static_cast<ACE_Reactor_Mask> (result)); + ACE_UNUSED_ARG (old_masks); + + // Get new masks + result = this->reactor ()->mask_ops (this, + ACE_Event_Handler::NULL_MASK, + ACE_Reactor::GET_MASK); + ACE_ASSERT (result != -1); + + ACE_Reactor_Mask current_masks = + ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK | + ACE_Event_Handler::EXCEPT_MASK; + + ACE_ASSERT (current_masks == + static_cast<ACE_Reactor_Mask> (result)); + ACE_UNUSED_ARG (current_masks); + + // Remove for writing + ACE_Reactor_Mask mask = ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::DONT_CALL; + result = this->reactor ()->remove_handler (this, + mask); + ACE_ASSERT (result == 0); + + // Write to the pipe; this causes handle_input to get called. + if (!write_to_pipe_in_main) + write_to_pipe (this->pipe_); + + return 0; + } + +protected: + ACE_Pipe &pipe_; +}; + + +// +// Selection of which reactor should get created +// +ACE_Reactor * +create_reactor (void) +{ + ACE_Reactor_Impl *impl = 0; + + if (opt_wfmo_reactor) + { +#if defined (ACE_WIN32) && !defined (ACE_HAS_WINCE) + ACE_NEW_RETURN (impl, + ACE_WFMO_Reactor, + 0); +#endif /* ACE_WIN32 */ + } + else if (opt_select_reactor) + { + ACE_NEW_RETURN (impl, + ACE_Select_Reactor, + 0); + } + else + { + ACE_Reactor *singleton_reactor = + ACE_Reactor::instance (); + ACE_Reactor::instance (0); + return singleton_reactor; + } + + ACE_Reactor *reactor = 0; + ACE_NEW_RETURN (reactor, + ACE_Reactor (impl, + 1), + 0); + + return reactor; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + int result = 0; + + // Parse args + ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("swmc"), 1); + for (int c; (c = getopt ()) != -1; ) + switch (c) + { + case 's': + opt_select_reactor = 1; + break; + case 'w': + opt_wfmo_reactor = 1; + break; + case 'm': + write_to_pipe_in_main = 1; + break; + case 'c': + cancel_reads = 1; + break; + } + + // Create pipes + ACE_Pipe pipe1, pipe2; + + result = pipe1.open (); + ACE_ASSERT (result == 0); + + result = pipe2.open (); + ACE_ASSERT (result == 0); + + // Create handlers + Handler handler (pipe1); + Different_Handler different_handler (pipe2); + + // Manage memory automagically. + auto_ptr<ACE_Reactor> reactor (create_reactor ()); + + // Register handlers + ACE_Reactor_Mask handler_mask = + ACE_Event_Handler::READ_MASK | + ACE_Event_Handler::WRITE_MASK | + ACE_Event_Handler::EXCEPT_MASK; + + ACE_Reactor_Mask different_handler_mask = + ACE_Event_Handler::WRITE_MASK | + ACE_Event_Handler::EXCEPT_MASK; + + result = reactor->register_handler (&handler, + handler_mask); + ACE_ASSERT (result == 0); + + result = reactor->register_handler (&different_handler, + different_handler_mask); + ACE_ASSERT (result == 0); + + // Write to the pipe; this causes handle_input to get called. + if (write_to_pipe_in_main) + { + write_to_pipe (pipe1); + write_to_pipe (pipe2); + } + + // Note that handle_output will get called automatically since the + // pipe is writable! + + // Run for three seconds + ACE_Time_Value time (3); + reactor->run_reactor_event_loop (time); + + ACE_DEBUG ((LM_DEBUG, "\nClosing down the application\n\n")); + + return 0; +} diff --git a/ACE/examples/Reactor/WFMO_Reactor/Makefile.am b/ACE/examples/Reactor/WFMO_Reactor/Makefile.am new file mode 100644 index 00000000000..cfa1c687b6c --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Makefile.am @@ -0,0 +1,308 @@ +## Process this file with automake to create Makefile.in +## +## $Id$ +## +## This file was generated by MPC. Any changes made directly to +## this file will be lost the next time it is generated. +## +## MPC Command: +## /acebuilds/ACE_wrappers-repository/bin/mwc.pl -include /acebuilds/MPC/config -include /acebuilds/MPC/templates -feature_file /acebuilds/ACE_wrappers-repository/local.features -noreldefs -type automake -exclude build,Kokyu + +ACE_BUILDDIR = $(top_builddir) +ACE_ROOT = $(top_srcdir) + +noinst_PROGRAMS = + +## Makefile.WFMO_Reactor_APC.am + +if BUILD_WFMO +if !BUILD_WINCE +noinst_PROGRAMS += apc + +apc_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +apc_SOURCES = \ + APC.cpp + +apc_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_WINCE +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Abandoned.am + +if BUILD_WFMO +noinst_PROGRAMS += abandoned + +abandoned_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +abandoned_SOURCES = \ + Abandoned.cpp + +abandoned_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Console_Input.am + +if BUILD_WFMO +noinst_PROGRAMS += console_input + +console_input_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +console_input_SOURCES = \ + Console_Input.cpp + +console_input_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Directory_Changes.am + +if BUILD_WFMO +noinst_PROGRAMS += directory_changes + +directory_changes_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +directory_changes_SOURCES = \ + Directory_Changes.cpp + +directory_changes_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Exceptions.am + +if BUILD_WFMO +noinst_PROGRAMS += exceptions + +exceptions_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +exceptions_SOURCES = \ + Exceptions.cpp + +exceptions_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Handle_Close.am + +if BUILD_WFMO +noinst_PROGRAMS += handle_close + +handle_close_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +handle_close_SOURCES = \ + Handle_Close.cpp + +handle_close_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Multithreading.am + +if BUILD_WFMO +noinst_PROGRAMS += multithreading + +multithreading_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +multithreading_SOURCES = \ + Multithreading.cpp + +multithreading_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Network_Events.am + +if BUILD_WFMO +noinst_PROGRAMS += network_events + +network_events_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +network_events_SOURCES = \ + Network_Events.cpp + +network_events_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Prerun_State_Changes.am + +if BUILD_WFMO +noinst_PROGRAMS += prerun_state_changes + +prerun_state_changes_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +prerun_state_changes_SOURCES = \ + Prerun_State_Changes.cpp + +prerun_state_changes_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Registration.am + +if BUILD_WFMO +noinst_PROGRAMS += registration + +registration_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +registration_SOURCES = \ + Registration.cpp + +registration_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Registry_Changes.am + +if BUILD_WFMO +if !BUILD_ACE_FOR_TAO +if !BUILD_WINCE +noinst_PROGRAMS += registry_changes + +registry_changes_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +registry_changes_SOURCES = \ + Registry_Changes.cpp + +registry_changes_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_WINCE +endif !BUILD_ACE_FOR_TAO +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Removals.am + +if BUILD_WFMO +noinst_PROGRAMS += removals + +removals_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +removals_SOURCES = \ + Removals.cpp + +removals_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Suspended_Removals.am + +if BUILD_WFMO +noinst_PROGRAMS += suspended_removals + +suspended_removals_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +suspended_removals_SOURCES = \ + Suspended_Removals.cpp + +suspended_removals_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Talker.am + +if BUILD_WFMO +if !BUILD_ACE_FOR_TAO +if !BUILD_WINCE +noinst_PROGRAMS += talker + +talker_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +talker_SOURCES = \ + Talker.cpp + +talker_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_WINCE +endif !BUILD_ACE_FOR_TAO +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Timeouts.am + +if BUILD_WFMO +noinst_PROGRAMS += timeouts + +timeouts_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +timeouts_SOURCES = \ + Timeouts.cpp + +timeouts_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif BUILD_WFMO + +## Makefile.WFMO_Reactor_Window_Messages.am + +if BUILD_WFMO +if !BUILD_ACE_FOR_TAO +if !BUILD_WINCE +noinst_PROGRAMS += window_messages + +window_messages_CPPFLAGS = \ + -I$(ACE_ROOT) \ + -I$(ACE_BUILDDIR) + +window_messages_SOURCES = \ + Window_Messages.cpp + +window_messages_LDADD = \ + $(ACE_BUILDDIR)/ace/libACE.la + +endif !BUILD_WINCE +endif !BUILD_ACE_FOR_TAO +endif BUILD_WFMO + +## Clean up template repositories, etc. +clean-local: + -rm -f *~ *.bak *.rpo *.sym lib*.*_pure_* core core.* + -rm -f gcctemp.c gcctemp so_locations *.ics + -rm -rf cxx_repository ptrepository ti_files + -rm -rf templateregistry ir.out + -rm -rf ptrepository SunWS_cache Templates.DB diff --git a/ACE/examples/Reactor/WFMO_Reactor/Multithreading.cpp b/ACE/examples/Reactor/WFMO_Reactor/Multithreading.cpp new file mode 100644 index 00000000000..0778f375251 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Multithreading.cpp @@ -0,0 +1,262 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Multithreading.cpp +// +// = DESCRIPTION +// +// This application tests multiple threads simultaneously calling +// Reactor::handle_events(). It also shows how different threads +// can update the state of Reactor by registering and removing +// Event_Handlers. +// +// Note that this test will only work with WFMO_Reactor +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Task.h" +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/Get_Opt.h" +#include "ace/OS_NS_time.h" + +ACE_RCSID(WFMO_Reactor, Multithreading, "$Id$") + +static int concurrent_threads = 1; +static int number_of_handles = static_cast<int> (ACE_Reactor::instance ()->size ()); +static int number_of_handles_to_signal = 1; +static int interval = 2; +static int iterations = 10; + +// Explain usage and exit. +static void +print_usage_and_die (void) +{ + ACE_DEBUG ((LM_DEBUG, + "usage: \n\t" + "[-t (# of threads - default 1)] \n\t" + "[-h (# of handlers) - default 62] \n\t" + "[-i (# time interval between signals) - default 2] \n\t" + "[-s (# of handles to signal) - default 1] \n\t" + "[-e (# of iterations) - default 10] \n\t")); + ACE_OS::exit (1); +} + +// Parse the command-line arguments and set options. +static void +parse_args (int argc, ACE_TCHAR **argv) +{ + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("t:h:s:i:e:")); + int c; + + while ((c = get_opt ()) != -1) + switch (c) + { + case 't': + concurrent_threads = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'e': + iterations = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'h': + number_of_handles = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 'i': + interval = ACE_OS::atoi (get_opt.opt_arg ()); + break; + case 's': + number_of_handles_to_signal = ACE_OS::atoi (get_opt.opt_arg ()); + break; + default: + print_usage_and_die (); + break; + } +} + +class Task_Handler : public ACE_Task<ACE_NULL_SYNCH> +{ +public: + Task_Handler (size_t number_of_handles, + size_t concurrent_threads); + // Constructor. + + ~Task_Handler (void); + // Destructor. + + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + // Called when object is removed from the ACE_Reactor + + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + // Handle events being signaled by the main thread. + + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg = 0); + // Called when timer expires. + + int svc (void); + // Task event loop. + + int signal (size_t index); + // Signal an event. + +private: + ACE_Auto_Event *events_; +}; + +// All threads do reactor->handle_events () +int +Task_Handler::svc (void) +{ + // Try to become the owner + ACE_Reactor::instance ()->owner (ACE_Thread::self ()); + // Run the event loop. + return ACE_Reactor::run_event_loop (); +} + +Task_Handler::Task_Handler (size_t number_of_handles, + size_t concurrent_threads) +{ + ACE_NEW (this->events_, ACE_Auto_Event [number_of_handles]); + + for (size_t i = 0; i < number_of_handles; ++i) + if (ACE_Reactor::instance ()->register_handler (this, + this->events_[i].handle ()) == -1) + ACE_ERROR ((LM_ERROR, + "%p\t cannot register handle %d with Reactor\n", + "Task_Handler::Task_Handler", + i)); + + // Make us an active object. + if (this->activate (THR_NEW_LWP, + static_cast<int> (concurrent_threads)) == -1) + ACE_ERROR ((LM_ERROR, "%p\t cannot activate task\n", + "activate")); +} + +Task_Handler::~Task_Handler (void) +{ + this->reactor (0); + delete [] this->events_; +} + + +int +Task_Handler::handle_signal (int, siginfo_t *siginfo, ucontext_t *) +{ + // When signaled, print message, remove self, and add self + // This will force Reactor to update its internal handle tables + + ACE_DEBUG ((LM_DEBUG, + "(%t) calls handle_signal for handle %d\n", + siginfo->si_handle_)); + + if (ACE_Reactor::instance ()->remove_handler (siginfo->si_handle_, + ACE_Event_Handler::DONT_CALL) == -1) + return -1; + // ACE_ERROR_RETURN ((LM_ERROR, + // "(%t) %p\tTask cannot be unregistered from Reactor: handle value = %d\n", + // "Task_Handler::handle_signal", + // siginfo->si_handle_), -1); + + if (ACE_Reactor::instance ()->register_handler (this, + siginfo->si_handle_) == -1) + return -1; + // ACE_ERROR_RETURN ((LM_ERROR, + // "(%t) %p\tTask cannot be registered with Reactor: handle value = %d\n", + // "Task_Handler::handle_signal", + // siginfo->si_handle_), -1); + return 0; +} + +int +Task_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) handle_close() called: handle value = %d\n", + handle)); + return 0; +} + +int +Task_Handler::handle_timeout (const ACE_Time_Value &, + const void *arg) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) handle_timeout() called: iteration value = %d\n", + size_t (arg))); + return 0; +} + +int +Task_Handler::signal (size_t index) +{ + return this->events_[index].signal (); +} + +int +ACE_TMAIN (int argc, ACE_TCHAR **argv) +{ + parse_args (argc, argv); + Task_Handler task (number_of_handles, + concurrent_threads); + + ACE_OS::srand (ACE_OS::time (0L)); + + for (int i = 1; i <= iterations; i++) + { + // Sleep for a while + ACE_OS::sleep (interval); + + // Randomly generate events + ACE_DEBUG ((LM_DEBUG, "********************************************************\n")); + ACE_DEBUG ((LM_DEBUG, "(%t -- main thread) signaling %d events : iteration = %d\n", + number_of_handles_to_signal, + i)); + ACE_DEBUG ((LM_DEBUG, "********************************************************\n")); + + // Setup a timer for the task + if (ACE_Reactor::instance ()->schedule_timer (&task, + (void *)((size_t)i), + ACE_Time_Value::zero) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "schedule_timer"), -1); + + for (int i = 0; i < number_of_handles_to_signal; i++) + // Randomly select a handle to signal. + task.signal (ACE_OS::rand() % number_of_handles); + } + + // Sleep for a while + ACE_OS::sleep (interval); + + // End the Reactor event loop + ACE_Reactor::end_event_loop (); + + // Wait for all threads to exit + ACE_Thread_Manager::instance ()->wait (); + + // Close the Reactor singleton before exiting this function. + // If we wait for the Object Manager to do this, it will be too + // late since Task_Handler instance would have disappeared. + ACE_Reactor::close_singleton (); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR **) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Network_Events.cpp b/ACE/examples/Reactor/WFMO_Reactor/Network_Events.cpp new file mode 100644 index 00000000000..9935679a60f --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Network_Events.cpp @@ -0,0 +1,211 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Network_Events.cpp +// +// = DESCRIPTION +// +// This application tests Reactor to make sure that it responds +// correctly to different kinds of network events. +// +// The test starts off by creating a Network_Listener, that listens +// for connections at ACE_DEFAULT_SERVER_PORT. When a client +// connects, a Network_Handler is created. Network_Handler reads +// messages off the socket and prints them out. This is done until +// the remote side shuts down. Multiple clients can connect at the +// same time. +// +// Events tested in this example includes ACCEPT, READ, and CLOSE masks. +// +// To run this example, start an instance of this example and +// connect to it using telnet (to port +// ACE_DEFAULT_SERVER_PORT(20002)). +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" +#include "ace/INET_Addr.h" +#include "ace/SOCK_Stream.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/OS_main.h" + +ACE_RCSID(WFMO_Reactor, Network_Events, "$Id$") + +class Network_Handler : public ACE_Event_Handler +{ +public: + Network_Handler (ACE_SOCK_Stream &s); + // Default constructor + + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + virtual ACE_HANDLE get_handle (void) const; + + ACE_SOCK_Stream stream_; + +}; + +Network_Handler::Network_Handler (ACE_SOCK_Stream &s) + : stream_ (s) +{ + this->reactor (ACE_Reactor::instance ()); + + int result = this->reactor ()->register_handler (this, READ_MASK); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); +} + +ACE_HANDLE +Network_Handler::get_handle (void) const +{ + return this->stream_.get_handle (); +} + +int +Network_Handler::handle_input (ACE_HANDLE handle) +{ + ACE_DEBUG ((LM_DEBUG, "Network_Handler::handle_input handle = %d\n", handle)); + + while (1) + { + char message[BUFSIZ]; + int result = this->stream_.recv (message, sizeof message); + if (result > 0) + { + message[result] = 0; + ACE_DEBUG ((LM_DEBUG, "Remote message: %s\n", message)); + } + else if (result == 0) + { + ACE_DEBUG ((LM_DEBUG, "Connection closed\n")); + return -1; + } + else if (errno == EWOULDBLOCK) + { + return 0; + } + else + { + ACE_DEBUG ((LM_DEBUG, "Problems in receiving data, result = %d", result)); + return -1; + } + } +} + +int +Network_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Network_Handler::handle_close handle = %d\n", handle)); + + this->stream_.close (); + delete this; + + ACE_Reactor::end_event_loop (); + + return 0; +} + +class Network_Listener : public ACE_Event_Handler +{ +public: + Network_Listener (void); + // Default constructor + ~Network_Listener (void); + // Default constructor + + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + ACE_HANDLE get_handle (void) const; + + ACE_INET_Addr local_address_; + ACE_SOCK_Acceptor acceptor_; +}; + +Network_Listener::Network_Listener (void) + : local_address_ (ACE_DEFAULT_SERVER_PORT), + acceptor_ (local_address_, 1) +{ + this->reactor (ACE_Reactor::instance ()); + int result = this->reactor ()->register_handler (this, + ACE_Event_Handler::ACCEPT_MASK); + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); +} + +Network_Listener::~Network_Listener (void) +{ +} + +ACE_HANDLE +Network_Listener::get_handle (void) const +{ + return this->acceptor_.get_handle (); +} + +int +Network_Listener::handle_input (ACE_HANDLE handle) +{ + ACE_DEBUG ((LM_DEBUG, "Network_Listener::handle_input handle = %d\n", handle)); + + ACE_INET_Addr remote_address; + ACE_SOCK_Stream stream; + + // Try to find out if the implementation of the reactor that we are + // using requires us to reset the event association for the newly + // created handle. This is because the newly created handle will + // inherit the properties of the listen handle, including its event + // associations. + int reset_new_handle = this->reactor ()->uses_event_associations (); + + int result = this->acceptor_.accept (stream, // stream + &remote_address, // remote address + 0, // timeout + 1, // restart + reset_new_handle); // reset new handler + ACE_ASSERT (result == 0); + ACE_UNUSED_ARG (result); + + ACE_DEBUG ((LM_DEBUG, "Remote connection from: ")); + remote_address.dump (); + + Network_Handler *handler; + ACE_NEW_RETURN (handler, Network_Handler (stream), -1); + + return 0; +} + +int +Network_Listener::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Network_Listener::handle_close handle = %d\n", handle)); + + this->acceptor_.close (); + + delete this; + + return 0; +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Network_Listener *listener = 0; + listener = new Network_Listener; + + ACE_Reactor::run_event_loop (); + + return 0; +}; diff --git a/ACE/examples/Reactor/WFMO_Reactor/Prerun_State_Changes.cpp b/ACE/examples/Reactor/WFMO_Reactor/Prerun_State_Changes.cpp new file mode 100644 index 00000000000..e276fd7d3f8 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Prerun_State_Changes.cpp @@ -0,0 +1,66 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Prerun_State_Changes.cpp +// +// = DESCRIPTION +// +// Tests the Reactor's ability to handle state changes before +// getting a chance to run. +// +// = AUTHOR +// +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/Reactor.h" +#include "ace/OS_main.h" + +ACE_RCSID(WFMO_Reactor, Prerun_State_Changes, "$Id$") + +class Event_Handler : public ACE_Event_Handler +// = TITLE +// Generic Event Handler. +// +{ +public: + virtual int handle_close (ACE_HANDLE handle, ACE_Reactor_Mask mask) + { + ACE_UNUSED_ARG(mask); + ACE_DEBUG ((LM_DEBUG, + "event handler %d closed.\n", + (size_t) handle)); + delete this; + return 0; + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_HANDLE handle = (ACE_HANDLE) ::socket (PF_INET, SOCK_STREAM, 0); + + Event_Handler *event_handler = new Event_Handler; + + int result = ACE_Reactor::instance ()->register_handler (handle, + event_handler, + ACE_Event_Handler::READ_MASK); + ACE_ASSERT (result == 0); + + result = ACE_Reactor::instance ()->register_handler (handle, + event_handler, + ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::QOS_MASK); + ACE_ASSERT (result == 0); + + result = ACE_Reactor::instance ()->remove_handler (handle, + ACE_Event_Handler::READ_MASK | ACE_Event_Handler::DONT_CALL); + ACE_ASSERT (result == 0); + + return 0; +} diff --git a/ACE/examples/Reactor/WFMO_Reactor/Registration.cpp b/ACE/examples/Reactor/WFMO_Reactor/Registration.cpp new file mode 100644 index 00000000000..a9429bdd433 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Registration.cpp @@ -0,0 +1,169 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Registration.cpp +// +// = DESCRIPTION +// +// This test application tests a wide range of registration, +// suspension, resumption, and removal of events from Reactor. +// +// The application initially registers two events with Reactor. A +// auxiliary thread is created to do the signaling on the +// events. When the first event is signaled, the event is suspended +// from Reactor. The event is then signaled again, but is "lost" +// since the handler has been suspended. When the second event is +// signal, the first event is resumed and the second is +// suspended. When the first event is signaled again, both events +// are removed from Reactor. +// +// This test shows off the following features of Reactor: +// - Registration +// - Suspension +// - Resumption +// - Removal (while active and while suspended) +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Auto_Event.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(WFMO_Reactor, Registration, "$Id$") + +// Globals for this test +int stop_test = 0; +ACE_Reactor reactor; + + +class Simple_Handler : public ACE_Event_Handler +{ +public: + Simple_Handler (void); + // Default constructor + + virtual int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + ACE_Auto_Event event1_; + ACE_Auto_Event event2_; + int handle_signal_count_; + int handle_close_count_; +}; + +Simple_Handler::Simple_Handler (void) + : handle_signal_count_ (0), + handle_close_count_ (0) +{ +} + +int +Simple_Handler::handle_signal (int, siginfo_t *s, ucontext_t *) +{ + ACE_HANDLE handle = s->si_handle_; + ACE_UNUSED_ARG (handle); + + this->handle_signal_count_++; + + if (this->handle_signal_count_ == 1) + this->reactor ()->suspend_handler (event1_.handle ()); + else if (this->handle_signal_count_ == 2) + { + this->reactor ()->resume_handler (event1_.handle ()); + this->reactor ()->suspend_handler (event2_.handle ()); + } + else if (this->handle_signal_count_ == 3) + { + this->reactor ()->remove_handler (event1_.handle (), + ACE_Event_Handler::NULL_MASK); + this->reactor ()->remove_handler (event2_.handle (), + ACE_Event_Handler::NULL_MASK); + } + return 0; +} + +int +Simple_Handler::handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Simple_Handler::handle_close handle = %d\n", handle)); + this->handle_close_count_++; + + if (this->handle_close_count_ == 1) + stop_test = 0; + else if (this->handle_close_count_ == 2) + stop_test = 1; + + return 0; +} + +// Globals for this test +Simple_Handler simple_handler; + +void +worker (void) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Thread creation\n")); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread signaling %d\n", simple_handler.event1_.handle())); + simple_handler.event1_.signal (); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread signaling %d\n", simple_handler.event1_.handle())); + ACE_DEBUG ((LM_DEBUG, "Note: This signal should be \"lost\" because of the suspended handler\n")); + simple_handler.event1_.signal (); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread resetting %d\n", simple_handler.event1_.handle())); + simple_handler.event1_.reset (); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread signaling %d\n", simple_handler.event2_.handle())); + simple_handler.event2_.signal (); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (1); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread signaling %d\n", simple_handler.event1_.handle())); + simple_handler.event1_.signal (); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread death\n")); +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + int result = reactor.register_handler (&simple_handler, + simple_handler.event1_.handle ()); + ACE_ASSERT (result == 0); + + result = reactor.register_handler (&simple_handler, + simple_handler.event2_.handle ()); + ACE_ASSERT (result == 0); + + result = ACE_OS::thr_create ((ACE_THR_FUNC) worker, 0, 0, 0); + ACE_ASSERT (result == 0); + + result = 0; + while (!stop_test && result != -1) + { + result = reactor.handle_events (); + } + return 0; +}; +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR **) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Registry_Changes.cpp b/ACE/examples/Reactor/WFMO_Reactor/Registry_Changes.cpp new file mode 100644 index 00000000000..be787293e65 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Registry_Changes.cpp @@ -0,0 +1,146 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Registry_Changes.cpp +// +// = DESCRIPTION +// +// This application tests the working of Reactor when users are +// interested in monitoring changes in the registry. +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Registry.h" +#include "ace/Auto_Event.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(WFMO_Reactor, Registry_Changes, "$Id$") + +static int stop_test = 0; +static HKEY context_to_monitor = HKEY_CURRENT_USER; +static const ACE_TCHAR *temp_context_name = ACE_TEXT ("ACE temporary context"); + +class Event_Handler : public ACE_Event_Handler +{ +public: + Event_Handler (ACE_Reactor &reactor); + ~Event_Handler (void); + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + ACE_Registry::Naming_Context &context (void); + +private: + ACE_Auto_Event event_; + ACE_Registry::Naming_Context context_; +}; + +Event_Handler::Event_Handler (ACE_Reactor &reactor) + : context_ (context_to_monitor) +{ + this->reactor (&reactor); + + if (::RegNotifyChangeKeyValue (this->context_.key (), // handle of key to watch + FALSE, // flag for subkey notification + REG_NOTIFY_CHANGE_NAME, // changes to be reported + this->event_.handle (), // handle of signaled event + TRUE // flag for asynchronous reporting + ) != ERROR_SUCCESS) + ACE_ERROR ((LM_ERROR, "RegNotifyChangeKeyValue could not be setup\n")); + + if (this->reactor ()->register_handler (this, + this->event_.handle ()) != 0) + ACE_ERROR ((LM_ERROR, "Registration with Reactor could not be done\n")); +} + +Event_Handler::~Event_Handler (void) +{ +} + +int +Event_Handler::handle_signal (int, siginfo_t *, ucontext_t *) +{ + if (stop_test) + this->reactor ()->close (); + else if (::RegNotifyChangeKeyValue (this->context_.key (), // handle of key to watch + FALSE, // flag for subkey notification + REG_NOTIFY_CHANGE_NAME, // changes to be reported + this->event_.handle (), // handle of signaled event + TRUE // flag for asynchronous reporting + ) != ERROR_SUCCESS) + ACE_ERROR ((LM_ERROR, + "RegNotifyChangeKeyValue could not be setup\n")); + return 0; +} + +int +Event_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "Event_Handler removed from Reactor\n")); + return 0; +} + +ACE_Registry::Naming_Context & +Event_Handler::context (void) +{ + return this->context_; +} + +void +worker (Event_Handler *event_handler) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Thread creation\n")); + ACE_DEBUG ((LM_DEBUG, "(%t) Thread creating temporary registry entry\n")); + + ACE_Registry::Naming_Context temp_context; + int result = event_handler->context ().bind_new_context (temp_context_name, + temp_context); + + if (result == -1) + ACE_ERROR ((LM_ERROR, "Error in creating %s: %p\n", temp_context_name, "bind_new_context")); + else + { + ACE_DEBUG ((LM_DEBUG, "(%t) Thread sleeping\n")); + ACE_OS::sleep (3); + + ACE_DEBUG ((LM_DEBUG, "(%t) Thread removing registry entry\n")); + stop_test = 1; + event_handler->context ().unbind_context (temp_context_name); + } +} + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + ACE_Reactor reactor; + Event_Handler handler (reactor); + + int result = ACE_OS::thr_create ((ACE_THR_FUNC) worker, &handler, 0, 0); + ACE_ASSERT (result == 0); + + for (result = 0; result != -1; result = reactor.handle_events ()) + continue; + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int, ACE_TCHAR **) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Removals.cpp b/ACE/examples/Reactor/WFMO_Reactor/Removals.cpp new file mode 100644 index 00000000000..260b9e897ee --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Removals.cpp @@ -0,0 +1,114 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Removals.cpp +// +// = DESCRIPTION +// +// Tests the Reactor's ability to handle simultaneous events. If +// you pass anything on the command-line, then each handler +// requests to be removed from the Reactor after each event. +// +// = AUTHOR +// Tim Harrison +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Service_Config.h" +#include "ace/Event.h" + +ACE_RCSID(WFMO_Reactor, Removals, "$Id$") + +class Event_Handler : public ACE_Event_Handler +// = TITLE +// Generic Event Handler. +// +// = DESCRIPTION +// +// Creates event. Registers with Reactor. Signals event. If +// created with -close_down- it returns -1 from handle signal. +{ +public: + Event_Handler (int event_number, + int close_down) + : event_number_ (event_number), + close_down_ (close_down) + { + if (ACE_Reactor::instance ()->register_handler (this, + this->event_.handle ()) == -1) + ACE_ERROR ((LM_ERROR, "%p\tevent handler %d cannot be added to Reactor\n", "", event_number_)); + this->event_.signal (); + } + + virtual int handle_signal (int, siginfo_t *, ucontext_t *) + { + if (this->close_down_) + return -1; + else + return 0; + } + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask) + { + ACE_DEBUG ((LM_DEBUG, "event handler %d closed.\n", event_number_)); + delete this; + return 0; + } + + virtual ACE_HANDLE get_handle (void) const + { + return event_.handle (); + } + +private: + int event_number_; + // Our event number. + + int close_down_; + // Shall we close down or not. + + ACE_Event event_; + // Signaled to shut down the handler. +}; + +int +ACE_TMAIN (int argc, ACE_TCHAR *[]) +{ + int close_down = argc > 1 ? 1 : 0; + + for (size_t i = 1; i <= ACE_Reactor::instance ()->size (); i++) + new Event_Handler (static_cast<int> (i), close_down); + + int result = 0; + ACE_Time_Value time (1); + while (1) + { + result = ACE_Reactor::instance ()->handle_events (time); + if (result == 0 && errno == ETIME) + { + ACE_DEBUG ((LM_DEBUG, "No more work left: timing out\n")); + break; + } + if (result == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); + } + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Suspended_Removals.cpp b/ACE/examples/Reactor/WFMO_Reactor/Suspended_Removals.cpp new file mode 100644 index 00000000000..ead467146a5 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Suspended_Removals.cpp @@ -0,0 +1,173 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Suspended_Removals.cpp +// +// = DESCRIPTION +// +// Tests the Reactor's ability to handle removal of suspended +// handles. +// +// = AUTHOR +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/WFMO_Reactor.h" + +ACE_RCSID(WFMO_Reactor, Suspended_Removals, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + + ACE_HANDLE get_handle (void) const + { + return this->event_.handle (); + } + + ACE_Event event_; +}; + +class ACE_WFMO_Reactor_Test +{ +public: + static void check_for_valid_state (ACE_WFMO_Reactor &wfmo_reactor, + size_t handles_to_be_added, + size_t handles_to_be_suspended, + size_t handles_to_be_resumed, + size_t handles_to_be_deleted) + { + ACE_ASSERT (wfmo_reactor.handler_rep_.handles_to_be_added_ == handles_to_be_added); + ACE_ASSERT (wfmo_reactor.handler_rep_.handles_to_be_suspended_ == handles_to_be_suspended); + ACE_ASSERT (wfmo_reactor.handler_rep_.handles_to_be_resumed_ == handles_to_be_resumed); + ACE_ASSERT (wfmo_reactor.handler_rep_.handles_to_be_deleted_ == handles_to_be_deleted); + } +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Event_Handler handler; + ACE_WFMO_Reactor reactor; + ACE_Reactor base_reactor (&reactor); + ACE_Time_Value time (1); + + int result = + reactor.register_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 1, 0, 0, 0); + + result = + reactor.remove_handler (&handler, + ACE_Event_Handler::DONT_CALL | + ACE_Event_Handler::ALL_EVENTS_MASK); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 1, 0, 0, 1); + + result = base_reactor.run_reactor_event_loop (time); + ACE_ASSERT (result != -1); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 0); + + result = + reactor.register_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 1, 0, 0, 0); + + result = base_reactor.run_reactor_event_loop (time); + ACE_ASSERT (result != -1); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 0); + + result = + reactor.suspend_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 1, 0, 0); + + result = + reactor.remove_handler (&handler, + ACE_Event_Handler::DONT_CALL | + ACE_Event_Handler::ALL_EVENTS_MASK); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 1); + + result = base_reactor.run_reactor_event_loop (time); + ACE_ASSERT (result != -1); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 0); + + result = + reactor.register_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 1, 0, 0, 0); + + result = + reactor.suspend_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 1, 1, 0, 0); + + result = base_reactor.run_reactor_event_loop (time); + ACE_ASSERT (result != -1); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 0); + + result = + reactor.resume_handler (&handler); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 1, 0); + + result = + reactor.remove_handler (&handler, + ACE_Event_Handler::DONT_CALL | + ACE_Event_Handler::ALL_EVENTS_MASK); + ACE_ASSERT (result == 0); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 1); + + result = base_reactor.run_reactor_event_loop (time); + ACE_ASSERT (result != -1); + + ACE_WFMO_Reactor_Test::check_for_valid_state (reactor, + 0, 0, 0, 0); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Talker.cpp b/ACE/examples/Reactor/WFMO_Reactor/Talker.cpp new file mode 100644 index 00000000000..32438088614 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Talker.cpp @@ -0,0 +1,594 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Talker.cpp +// +// = DESCRIPTION +// +// This test application tests a wide range of events that can be +// demultiplexed using various ACE utilities. Events used include +// ^C events, reading from STDIN, vanilla Win32 events, thread +// exits, Reactor notifications, proactive reads, and proactive +// writes. +// +// The proactive I/O events are demultiplexed by the ACE_Proactor. +// The thread exits, notications, and vanilla Win32 events are +// demultiplexed by the ACE_Reactor. To enable a single thread +// to run all these events, the Proactor is integrated with the +// Reactor. +// +// The test application prototypes a simple talk program. Two +// instances of the application connect. Input from either console +// is displayed on the others console also. Because of the evils +// of Win32 STDIN, a separate thread is used to read from STDIN. +// To test the Proactor and Reactor, I/O between the remote +// processes is performed proactively and interactions between the +// STDIN thread and the main thread are performed reactively. +// +// The following description of the test application is in two +// parts. The participants section explains the main components +// involved in the application. The collaboration section +// describes how the partipants interact in response to the +// multiple event types which occur. +// +// The Reactor test application has the following participants: +// +// . Reactor -- The Reactor demultiplexes Win32 "waitable" +// events using WaitForMultipleObjects. +// +// . Proactor -- The proactor initiates and demultiplexes +// overlapped I/O operations. The Proactor registers with the +// Reactor so that a single-thread can demultiplex all +// application events. +// +// . STDIN_Handler -- STDIN_Handler is an Active Object which reads +// from STDIN and forwards the input to the Peer_Handler. This +// runs in a separate thread to make the test more interesting. +// However, STDIN is "waitable", so in general it can be waited on +// by the ACE Reactor, thanks MicroSlush! +// +// . Peer_Handler -- The Peer_Handler connects to another instance +// of test_reactor. It Proactively reads and writes data to the +// peer. When the STDIN_Handler gives it messages, it fowards them +// to the remote peer. When it receives messages from the remote +// peer, it prints the output to the console. +// +// The collaborations of the participants are as follows: +// +// . Initialization +// +// Peer_Handler -- connects to the remote peer. It then begins +// proactively reading from the remote connection. Note that it +// will be notified by the Proactor when a read completes. It +// also registers a notification strategy with message queue so +// that it is notified when the STDIN_Handler posts a message +// onto the queue. +// +// STDIN_Handler -- STDIN_Handler registers a signal handler for +// SIGINT. This just captures the exception so that the kernel +// doesn't kill our process; We want to exit gracefully. It also +// creates an Exit_Hook object which registers the +// STDIN_Handler's thread handle with the Reactor. The +// Exit_Hook will get called back when the STDIN_Handler thread +// exits. After registering these, it blocks reading from STDIN. +// +// Proactor -- is registered with the Reactor. +// +// The main thread of control waits in the Reactor. +// +// . STDIN events -- When the STDIN_Handler thread reads from +// STDIN, it puts the message on Peer_Handler's message queue. It +// then returns to reading from STDIN. +// +// . Message enqueue -- The Reactor thread wakes up and calls +// Peer_Handler::handle_output. The Peer_Handler then tries to +// dequeue a message from its message queue. If it can, the +// message is Proactively sent to the remote peer. Note that the +// Peer_Handler will be notified with this operation is complete. +// The Peer_Handler then falls back into the Reactor event loop. +// +// . Send complete event -- When a proactive send is complete, the +// Proactor is notified by the Reactor. The Proactor, in turn, +// notifies the Peer_Handler. The Peer_Handler then checks for +// more messages from the message queue. If there are any, it +// tries to send them. If there are not, it returns to the +// Reactor event loop. +// +// . Read complete event -- When a proactive read is complete (the +// Peer_Handler initiated a proactive read when it connected to the +// remote peer), the Proactor is notified by the Reactor. The +// Proactor, in turn notifies the Peer_Handler. If the read was +// successful the Peer_Handler just displays the received msg to +// the console and reinvokes a proactive read from the network +// connection. If the read failed (i.e. the remote peer exited), +// the Peer_Handler sets a flag to end the event loop and returns. +// This will cause the application to exit. +// +// . ^C events -- When the user types ^C at the console, the +// STDIN_Handler's signal handler will be called. It does nothing, +// but as a result of the signal, the STDIN_Handler thread will +// exit. +// +// . STDIN_Handler thread exits -- The Exit_Hook will get called +// back from the Reactor. Exit_Hook::handle_signal sets a flag +// to end the event loop and returns. This will cause the +// application to exit. +// +// +// To run example, start an instance of the test with an optional +// local port argument (as the acceptor). Start the other instance +// with -h <hostname> and -p <server port>. Type in either the +// client or server windows and your message should show up in the +// other window. Control C to exit. +// +// = AUTHOR +// Tim Harrison +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Reactor.h" +#include "ace/Reactor_Notification_Strategy.h" +#include "ace/WIN32_Proactor.h" +#include "ace/Proactor.h" +#include "ace/SOCK_Connector.h" +#include "ace/SOCK_Acceptor.h" +#include "ace/Get_Opt.h" +#include "ace/Service_Config.h" +#include "ace/Task.h" +#include "ace/OS_NS_unistd.h" + +ACE_RCSID(WFMO_Reactor, Talker, "$Id$") + +typedef ACE_Task<ACE_MT_SYNCH> MT_TASK; + +class Peer_Handler : public MT_TASK, public ACE_Handler +// = TITLE +// Connect to a server. Receive messages from STDIN_Handler +// and forward them to the server using proactive I/O. +{ +public: + // = Initialization methods. + Peer_Handler (int argc, ACE_TCHAR *argv[]); + ~Peer_Handler (void); + + int open (void * =0); + // This method creates the network connection to the remote peer. + // It does blocking connects and accepts depending on whether a + // hostname was specified from the command line. + + virtual void handle_read_stream (const ACE_Asynch_Read_Stream::Result &result); + // This method will be called when an asynchronous read completes on a stream. + // The remote peer has sent us something. If it succeeded, print + // out the message and reinitiate a read. Otherwise, fail. In both + // cases, delete the message sent. + + virtual void handle_write_stream (const ACE_Asynch_Write_Stream::Result &result); + // This method will be called when an asynchronous write completes on a strea_m. + // One of our asynchronous writes to the remote peer has completed. + // Make sure it succeeded and then delete the message. + + virtual ACE_HANDLE handle (void) const; + // Get the I/O handle used by this <handler>. This method will be + // called by the ACE_Asynch_* classes when an ACE_INVALID_HANDLE is + // passed to <open>. + + void handle (ACE_HANDLE); + // Set the ACE_HANDLE value for this Handler. + + virtual int handle_close (ACE_HANDLE, ACE_Reactor_Mask); + // We've been removed from the Reactor. + + virtual int handle_output (ACE_HANDLE fd); + // Called when output events should start. Note that this is + // automatically invoked by the + // <ACE_Reactor_Notificiation_Strategy>. + +private: + ACE_SOCK_Stream stream_; + // Socket that we have connected to the server. + + ACE_Reactor_Notification_Strategy strategy_; + // The strategy object that the reactor uses to notify us when + // something is added to the queue. + + // = Remote peer info. + ACE_TCHAR *host_; + // Name of remote host. + + u_short port_; + // Port number for remote host. + + ACE_Asynch_Read_Stream rd_stream_; + // Read stream + + ACE_Asynch_Write_Stream wr_stream_; + // Write stream + + ACE_Message_Block mb_; + // Message Block for reading from the network +}; + +class STDIN_Handler : public ACE_Task<ACE_NULL_SYNCH> +// = TITLE +// Active Object. Reads from STDIN and passes message blocks to +// the peer handler. +{ +public: + STDIN_Handler (MT_TASK &ph); + // Initialization. + + virtual int open (void * = 0); + // Activate object. + + virtual int close (u_long = 0); + // Shut down. + + int svc (void); + // Thread runs here as an active object. + + int handle_close (ACE_HANDLE, + ACE_Reactor_Mask); + +private: + static void handler (int signum); + // Handle a ^C. (Do nothing, this just illustrates how we can catch + // signals along with the other things). + + void register_thread_exit_hook (void); + // Helper function to register with the Reactor for thread exit. + + virtual int handle_signal (int index, siginfo_t *, ucontext_t *); + // The STDIN thread has exited. This means the user hit ^C. We can + // end the event loop. + + MT_TASK &ph_; + // Send all input to ph_. + + ACE_HANDLE thr_handle_; + // Handle of our thread. +}; + +Peer_Handler::Peer_Handler (int argc, ACE_TCHAR *argv[]) + : strategy_ (ACE_Reactor::instance (), + this, + ACE_Event_Handler::WRITE_MASK), + host_ (0), + port_ (ACE_DEFAULT_SERVER_PORT), + mb_ (BUFSIZ) +{ + // This code sets up the message to notify us when a new message is + // added to the queue. Actually, the queue notifies Reactor which + // then notifies us. + this->msg_queue ()->notification_strategy (&this->strategy_); + + ACE_Get_Opt get_opt (argc, argv, ACE_TEXT("h:p:")); + int c; + + while ((c = get_opt ()) != EOF) + { + switch (c) + { + case 'h': + host_ = get_opt.opt_arg (); + break; + case 'p': + port_ = ACE_OS::atoi (get_opt.opt_arg ()); + break; + } + } +} + +Peer_Handler::~Peer_Handler (void) +{ +} + +// This method creates the network connection to the remote peer. It +// does blocking connects and accepts depending on whether a hostname +// was specified from the command line. + +int +Peer_Handler::open (void *) +{ + if (host_ != 0) // Connector + { + ACE_INET_Addr addr (port_, host_); + ACE_SOCK_Connector connector; + + // Establish connection with server. + if (connector.connect (stream_, addr) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "connect"), -1); + + ACE_DEBUG ((LM_DEBUG, "(%t) connected.\n")); + } + else // Acceptor + { + ACE_SOCK_Acceptor acceptor; + ACE_INET_Addr local_addr (port_); + + if ((acceptor.open (local_addr) == -1) || + (acceptor.accept (this->stream_) == -1)) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "accept failed"), -1); + + ACE_DEBUG ((LM_DEBUG, "(%t) accepted.\n")); + } + + int result = this->rd_stream_.open (*this); + if (result != 0) + return result; + + result = this->wr_stream_.open (*this); + if (result != 0) + return result; + + result = this->rd_stream_.read (this->mb_, + this->mb_.size ()); + return result; +} + +// One of our asynchronous writes to the remote peer has completed. +// Make sure it succeeded and then delete the message. + +void +Peer_Handler::handle_write_stream (const ACE_Asynch_Write_Stream::Result &result) +{ + if (result.bytes_transferred () <= 0) + ACE_DEBUG ((LM_DEBUG, "(%t) %p bytes = %d\n", "Message failed", + result.bytes_transferred ())); + + // This was allocated by the STDIN_Handler, queued, dequeued, passed + // to the proactor, and now passed back to us. + result.message_block ().release (); +} + +// The remote peer has sent us something. If it succeeded, print +// out the message and reinitiate a read. Otherwise, fail. In both +// cases, delete the message sent. + + +void +Peer_Handler::handle_read_stream (const ACE_Asynch_Read_Stream::Result &result) +{ + if (result.bytes_transferred () > 0 && + this->mb_.length () > 0) + { + this->mb_.rd_ptr ()[result.bytes_transferred ()] = '\0'; + // Print out the message received from the server. + ACE_DEBUG ((LM_DEBUG, "%s", this->mb_.rd_ptr ())); + } + else + { + // If a read failed, we will assume it's because the remote peer + // went away. We will end the event loop. Since we're in the + // main thread, we don't need to do a notify. + ACE_Reactor::end_event_loop(); + return; + } + + // Reset pointers + this->mb_.wr_ptr (this->mb_.wr_ptr () - result.bytes_transferred ()); + + // Start off another read + if (this->rd_stream_.read (this->mb_, + this->mb_.size ()) == -1) + ACE_ERROR ((LM_ERROR, "%p Read initiate.\n", "Peer_Handler")); +} + +// This is so the Proactor can get our handle. +ACE_HANDLE +Peer_Handler::handle (void) const +{ + return this->stream_.get_handle (); +} + +void +Peer_Handler::handle (ACE_HANDLE handle) +{ + this->stream_.set_handle (handle); +} + +// We've been removed from the Reactor. +int +Peer_Handler::handle_close (ACE_HANDLE, ACE_Reactor_Mask) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) Peer_Handler closing down\n")); + return 0; +} + +// New stuff added to the message queue. Try to dequeue a message. +int +Peer_Handler::handle_output (ACE_HANDLE) +{ + ACE_Message_Block *mb; + + ACE_Time_Value tv (ACE_Time_Value::zero); + + // Forward the message to the remote peer receiver. + if (this->getq (mb, &tv) != -1) + { + if (this->wr_stream_.write (*mb, + mb->length ()) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p Write initiate.\n", "Peer_Handler"), -1); + } + return 0; +} + +void +STDIN_Handler::handler (int signum) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) signal = %S\n", signum)); +} + +STDIN_Handler::STDIN_Handler (MT_TASK &ph) + : ph_ (ph) +{ + // Register for ^C from the console. We just need to catch the + // exception so that the kernel doesn't kill our process. + // Registering this signal handler just tells the kernel that we + // know what we're doing; to leave us alone. + + ACE_OS::signal (SIGINT, (ACE_SignalHandler) STDIN_Handler::handler); +}; + +// Activate object. + +int +STDIN_Handler::open (void *) +{ + if (this->activate (THR_NEW_LWP | THR_DETACHED) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p\n", "spawn"), -1); + + return 0; +} + +// Shut down. + +int +STDIN_Handler::close (u_long) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) thread is exiting.\n")); + return 0; +} + +// Thread runs here. + +int +STDIN_Handler::svc (void) +{ + this->register_thread_exit_hook (); + + for (;;) + { + ACE_Message_Block *mb = new ACE_Message_Block (BUFSIZ); + + // Read from stdin into mb. + int read_result = ACE_OS::read (ACE_STDIN, + mb->rd_ptr (), + mb->size ()); + + // If read succeeds, put mb to peer handler, else end the loop. + if (read_result > 0) + { + mb->wr_ptr (read_result); + // Note that this call will first enqueue mb onto the peer + // handler's message queue, which will then turn around and + // notify the Reactor via the Notification_Strategy. This + // will subsequently signal the Peer_Handler, which will + // react by calling back to its handle_output() method, + // which dequeues the message and sends it to the peer + // across the network. + this->ph_.putq (mb); + } + else + { + mb->release (); + break; + } + } + + // handle_signal will get called on the main proactor thread since + // we just exited and the main thread is waiting on our thread exit. + return 0; +} + +// Register an exit hook with the reactor. + +void +STDIN_Handler::register_thread_exit_hook (void) +{ + // Get a real handle to our thread. + ACE_Thread_Manager::instance ()->thr_self (this->thr_handle_); + + // Register ourselves to get called back when our thread exits. + + if (ACE_Reactor::instance ()-> + register_handler (this, this->thr_handle_) == -1) + ACE_ERROR ((LM_ERROR, "Exit_Hook Register failed.\n")); +} + +// The STDIN thread has exited. This means the user hit ^C. We can +// end the event loop and delete ourself. + +int +STDIN_Handler::handle_signal (int, siginfo_t *si, ucontext_t *) +{ + if (si != 0) + { + ACE_ASSERT (this->thr_handle_ == si->si_handle_); + ACE_Reactor::end_event_loop (); + } + return 0; +} + +int +STDIN_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + delete this; + return 0; +} + +int +ACE_TMAIN (int argc, ACE_TCHAR *argv[]) +{ + // Let the proactor know that it will be used with Reactor + // Create specific proactor + ACE_WIN32_Proactor win32_proactor (0, 1); + // Get the interface proactor + ACE_Proactor proactor (&win32_proactor); + // Put it as the instance. + ACE_Proactor::instance (&proactor); + + // Open handler for remote peer communications this will run from + // the main thread. + Peer_Handler peer_handler (argc, argv); + + if (peer_handler.open () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p open failed, errno = %d.\n", + "peer_handler", errno), 0); + + // Open active object for reading from stdin. + STDIN_Handler *stdin_handler = + new STDIN_Handler (peer_handler); + + // Spawn thread. + if (stdin_handler->open () == -1) + ACE_ERROR_RETURN ((LM_ERROR, + "%p open failed, errno = %d.\n", + "stdin_handler", errno), 0); + + // Register proactor with Reactor so that we can demultiplex + // "waitable" events and I/O operations from a single thread. + if (ACE_Reactor::instance ()->register_handler + (ACE_Proactor::instance ()->implementation ()) != 0) + ACE_ERROR_RETURN ((LM_ERROR, "%p failed to register Proactor.\n", + argv[0]), -1); + + // Run main event demultiplexor. + ACE_Reactor::run_event_loop (); + + // Remove proactor with Reactor. + if (ACE_Reactor::instance ()->remove_handler + (ACE_Proactor::instance ()->implementation (), ACE_Event_Handler::DONT_CALL) != 0) + ACE_ERROR_RETURN ((LM_ERROR, "%p failed to register Proactor.\n", + argv[0]), -1); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/Timeouts.cpp b/ACE/examples/Reactor/WFMO_Reactor/Timeouts.cpp new file mode 100644 index 00000000000..8cc37a940bc --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Timeouts.cpp @@ -0,0 +1,83 @@ +// $Id$ +// +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Timeouts.cpp +// +// = DESCRIPTION +// +// This example application shows how to write Reactor event +// loops that handle events for some fixed amount of time. +// +// Run this example (without arguments) to see the timers +// expire. The order should be: +// +// foo, bar, foo, bar, foo, foo, bar, foo, bar, foo +// +// = AUTHOR +// Tim Harrison +// Irfan Pyarali +// +// ============================================================================ + +#include "ace/Reactor.h" +#include "ace/Service_Config.h" +#include "ace/OS_main.h" + +ACE_RCSID(WFMO_Reactor, Timeouts, "$Id$") + +class Timeout_Handler : public ACE_Event_Handler +// = TITLE +// Generic timeout handler. +{ +public: + Timeout_Handler (void) + : count_ (0) {} + + virtual int handle_timeout (const ACE_Time_Value &tv, + const void *arg) + // Print out when timeouts occur. + { + ACE_UNUSED_ARG(tv); + ACE_DEBUG ((LM_DEBUG, + "%d timeout occurred for %s.\n", + ++count_, + (char *) arg)); + return 0; + } + +private: + int count_; +}; + +int +ACE_TMAIN (int, ACE_TCHAR *[]) +{ + Timeout_Handler handler; + + // Register a 3 second timer. + ACE_Time_Value bar_tv (3); + ACE_Reactor::instance ()->schedule_timer (&handler, + (void *) "Bar", + bar_tv, + bar_tv); + + // Register a 2 second timer. + ACE_Time_Value foo_tv (2); + ACE_Reactor::instance ()->schedule_timer (&handler, + (void *) "Foo", + foo_tv, + foo_tv); + // Handle events for 12 seconds. + ACE_Time_Value run_time (12); + if (ACE_Reactor::run_event_loop(run_time) == -1) + ACE_ERROR_RETURN ((LM_ERROR, "%p.\n", "main"), -1); + + ACE_Reactor::instance ()->cancel_timer(&handler); + + return 0; +} diff --git a/ACE/examples/Reactor/WFMO_Reactor/WFMO_Reactor.mpc b/ACE/examples/Reactor/WFMO_Reactor/WFMO_Reactor.mpc new file mode 100644 index 00000000000..eec632ee696 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/WFMO_Reactor.mpc @@ -0,0 +1,118 @@ +// -*- MPC -*- +// $Id$ + +project(*Abandoned): aceexe, wfmo { + exename = abandoned + Source_Files { + Abandoned.cpp + } +} + +project(*APC): aceexe, wfmo { + avoids += wince + exename = apc + Source_Files { + APC.cpp + } +} + +project(*Console_Input): aceexe, wfmo { + exename = console_input + Source_Files { + Console_Input.cpp + } +} + +project(*Directory_Changes): aceexe, wfmo { + exename = directory_changes + Source_Files { + Directory_Changes.cpp + } +} + +project(*Exceptions): aceexe, wfmo { + exename = exceptions + Source_Files { + Exceptions.cpp + } +} + +project(*Handle_Close): aceexe, wfmo { + exename = handle_close + Source_Files { + Handle_Close.cpp + } +} + +project(*Multithreading): aceexe, wfmo { + exename = multithreading + Source_Files { + Multithreading.cpp + } +} + +project(*Network_Events): aceexe, wfmo { + exename = network_events + Source_Files { + Network_Events.cpp + } +} + +project(*Prerun_State_Changes): aceexe, wfmo { + exename = prerun_state_changes + Source_Files { + Prerun_State_Changes.cpp + } +} + +project(*Registration): aceexe, wfmo { + exename = registration + Source_Files { + Registration.cpp + } +} + +project(*Registry_Changes): aceexe, wfmo { + avoids += ace_for_tao wince + exename = registry_changes + Source_Files { + Registry_Changes.cpp + } +} + +project(*Removals): aceexe, wfmo { + exename = removals + Source_Files { + Removals.cpp + } +} + +project(*Suspended_Removals): aceexe, wfmo { + exename = suspended_removals + Source_Files { + Suspended_Removals.cpp + } +} + +project(*Talker): aceexe, wfmo { + avoids += ace_for_tao wince + exename = talker + Source_Files { + Talker.cpp + } +} + +project(*Timeouts): aceexe, wfmo { + exename = timeouts + Source_Files { + Timeouts.cpp + } +} + +project(*Window_Messages): aceexe, wfmo { + avoids += ace_for_tao wince + exename = window_messages + Source_Files { + Window_Messages.cpp + } +} diff --git a/ACE/examples/Reactor/WFMO_Reactor/Window_Messages.cpp b/ACE/examples/Reactor/WFMO_Reactor/Window_Messages.cpp new file mode 100644 index 00000000000..f5a1994168c --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/Window_Messages.cpp @@ -0,0 +1,100 @@ +// $Id$ + +// ============================================================================ +// +// = LIBRARY +// examples +// +// = FILENAME +// Window_Messages.cpp +// +// = DESCRIPTION +// +// Tests the Msg_WFMO_Reactor's ability to handle regular events +// and window messages. +// +// = AUTHOR +// +// Irfan Pyarali <irfan@cs.wustl.edu> +// +// ============================================================================ + +#include "ace/OS_main.h" + +#if defined (ACE_WIN32) + +#include "ace/Msg_WFMO_Reactor.h" +#include "ace/Reactor.h" +#include "ace/Auto_Ptr.h" +#include "ace/Auto_Event.h" + +ACE_RCSID(WFMO_Reactor, Window_Messages, "$Id$") + +class Event_Handler : public ACE_Event_Handler +{ +public: + int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0); + + ACE_Auto_Event handle_; + int iterations_; +}; + +int +Event_Handler::handle_signal (int, siginfo_t *, ucontext_t *) +{ + --this->iterations_; + + if (this->iterations_ == 0) + ACE_Reactor::end_event_loop (); + + return 0; +} + +static Event_Handler *global_event_handler; + +void WINAPI +timer_callback (HWND, + UINT, + UINT, + DWORD dwTime) +{ + ACE_DEBUG ((LM_DEBUG, "(%t) timeout occured @ %u\n", dwTime)); + + global_event_handler->handle_.signal (); +} + +int +ACE_TMAIN (int, ACE_TCHAR*[]) +{ + // Manage memory automagically. + ACE_Reactor_Impl *impl = new ACE_Msg_WFMO_Reactor; + auto_ptr<ACE_Reactor> reactor (new ACE_Reactor (impl, 1)); + ACE_Reactor::instance (reactor.get ()); + + Event_Handler event_handler; + global_event_handler = &event_handler; + + event_handler.iterations_ = 5; + int result = ACE_Reactor::instance ()->register_handler (&event_handler, + event_handler.handle_.handle ()); + ACE_ASSERT (result == 0); + + ACE_Time_Value timeout (1); + result = ::SetTimer (NULL, // handle of window for timer messages + 0, // timer identifier + timeout.msec (), // time-out value + (TIMERPROC) &timer_callback // address of timer procedure + ); + ACE_ASSERT (result != 0); + + ACE_Reactor::run_event_loop (); + + return 0; +} +#else /* !ACE_WIN32 */ +int +ACE_TMAIN (int , ACE_TCHAR *[]) +{ + return 0; +} +#endif /* ACE_WIN32 */ diff --git a/ACE/examples/Reactor/WFMO_Reactor/run_test.pl b/ACE/examples/Reactor/WFMO_Reactor/run_test.pl new file mode 100755 index 00000000000..cc445c64043 --- /dev/null +++ b/ACE/examples/Reactor/WFMO_Reactor/run_test.pl @@ -0,0 +1,68 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' + & eval 'exec perl -S $0 $argv:q' + if 0; + +# $Id$ +# -*- perl -*- + +use lib '../../../bin'; +use PerlACE::Run_Test; + +# +# These tests only run on Win32 +# +if ($^O ne "MSWin32") +{ + exit; +} + +$test_timeout = 60; + +@tests = + ( + "Abandoned", + "APC", +# "Console_Input", # This test is interactive + "Directory_Changes", + "Exceptions", + "Handle_Close", + "Multithreading", +# "Network_Events", # This test is interactive + "Prerun_State_Changes", + "Registration", + "Registry_Changes", + "Removals", + "Suspended_Removals", +# "Talker", # This test is interactive + "Timeouts", + "Window_Messages", + ); + +for $test (@tests) +{ + print STDOUT "\n________________________________________\n"; + print STDOUT "\nStarting test \"$test\""; + print STDOUT "\n________________________________________\n\n"; + + $test_process = new PerlACE::Process ($test); + + if (! -x $test_process->Executable ()) { + print STDERR "Error: " . $test_process->Executable () . + " does not exist or is not runnable\n"; + } + else + { + $test_process->Spawn (); + $test_result = $test_process->WaitKill ($test_timeout); + + if ($test_result != 0) + { + print STDERR "\n________________________________________\n"; + print STDERR "\nERROR: \"$test\" returned $test_result"; + print STDERR "\n________________________________________\n"; + } + } + print STDOUT "\n________________________________________\n"; + print STDOUT "\n\"$test\" completed"; + print STDOUT "\n________________________________________\n"; +} |