Doug (schmidt@cs.wm.edu)
General details on ACE.
The ADAPTIVE
Communication Environment (ACE) is a high-level
C++ toolkit for writing sophisticated concurrent, parallel,
and distributed applications. It was developed by Douglas C. Schmidt.
ACE is freely available and you can obtain the source code,
documentation,
and other items of related interest
On which platforms has ACE been ported and tested?
Please see
the installation
file in the ACE release for more details on platforms that ACE
runs on.
Where's the latest ACE version?
The current version of ACE can be obtained electronically via ftp and the WWW. It is about about 6
Meg, compressed using GNU gzip. There are many changes and
improvements in this version of ACE. The ChangeLog file contains complete details about
all of them.
SIGHUP
Where does the HUP signal get registered for the
$ACE_ROOT/tests/Service_Configurator/server stuff? I looked there
and in $ACE_ROOT/libsrc/Service_Configurator. No luck. I guess I
am just blind from reading.
Multi-threaded Signal_Handler support
It appears Signal_Handler is not setup for multi-threaded apps. How
do you handle signals in different threads? Do I have to put in the
hooks in my app or should it go in the Threads arena?
The design in ACE follows the approach espoused by Sun. Basically,
they suggest that you implement per-thread signal handling atop of the
basic UNIX signal handlers (or in the case of ACE, the handle_signal()
callbacks on Event_Handler subclasses) by using the thread id returned
by thr_self() to index into a search structure containing the
handlers. This should be pretty straight forward to layer atop the
existing ACE Signal_Handler mechanisms. However, you might ask
yourself whether you really want (1) separate signal handler
*functionality* in different threads or (2) different threads that
mask out certain signals. The latter might be easier to implement and
reason about!
Problems compiling ACE with G++
I substituted -lg++ for -lC in macro_wrappers.GNU and ran make.
Most stuff seemed to build. Continually got messages like the following:
Yes, that's exactly right. If you look at the files, they all contain
ifdef's for features that aren't included in the
./include/makeinclude/wrapper_macros.GNU file. To make this more
obvious, I've enclosed the following message in the INSTALL file:
Yes, if you use the stock gcc/gas/gnu ld compiler/assembler/linker,
you won't get shared libraries to work. It is possible to hack this
by using the "collect" version of g++. This has been done
successfully for GCC 2.7.1 on Solaris 2.x. We're working on making
this work on other platforms GCC has been ported to.
Are there any docs or man pages on the Log_Record class?
Manual pages for all of the ACE classes are in ./man/man3. Chapter 4
of our book C++
Network Programming: Mastering Complexity with ACE and Patterns
has some examples of using Log_Record. The ./apps/Logger directories
show several examples using Log_Record. Finally, the source code for
Log_Record is pretty short.
Signal handling prototypes
What is ACE_SignalHandlerV and why is it used in various places
(like the libsrc/Reactor/Signal.C file)? According to the man page on
sigaction on our system, that line should look something like the
following:
Omitting shared libraries
Can anyone tell me a way to turn off the creation of the shared
libraries in the ACE build?
You can simply comment out the LIB target in the
$ACE_ROOT/ace/Makefile or change the BUILD target from
DCE threading and signal handling.
Reading the DCE docs leaves me confused as to how to make everyone
work together in a happy harmonious whole. May basic need is to catch
asynchronous signals so i can release some global resources before the
process exits.
You need to spawn a separate thread to handle signals. As part of
your init, do this:
Explicit dynamic linking on SunOS with gcc.
I have installed ACE2.15.5 on SunOS 4.1.3 with gcc2.6.0. I run the test program
---server_test. The static is OK, but error found when I commented out the first
one and uncommented out the second one in the svc.conf file:
#static Svc_Manager "-d -p 3912"
The error goes like this:
My guess is that the code generated by GCC on SunOS 4.x does not
correctly initialize static variables from shared libraries. The
SunC++ 4.0.x compiler does this correctly on Solaris 2.x (though I
believe that on SunOS 4.x it doesn't work without some extra
coaxing).
In general, I try to avoid using ACE's explicit dynamic linking
mechanisms on SunOS 4.x and GCC. You can write plenty of interesting
and useful code with ACE without using those features. Those tests
are mostly there to illustrate the "proof of concept."
Re-Opening ACE_Reactor
I noticed the default constructor for the ACE_Reactor does an
open w/ defaults. Does this mean I need to close it if I wish to
re-open it with different size and restart values?
No. With the latest versions of ACE, you can now just call
open() with a new size and it will correctly resize the internal
tables to fit.
Difference between FD_Set rd/wr/ex_handle_mask_ and
rd/wr/ex_handle_mask_ready.
What is the usage difference between the normal FD_Set objects
(rd/wr/ex_handle_mask_) and the ready FD_Set objects
(rd/wr/ex_handle_mask_ready)?
The normal FD_Sets (now called Handle_Set in ACE 3.0.5) holds
the "waitable" descriptors (these are the descriptors given to
select() or poll()). In contrast, the ready FD_Sets may be set by
Event_Handler subclasses (by called the set_ready() API) to indicate
to the Reactor that they want to be redispatched on the next go-round
*without* blocking. If you look at the Reactor code, you'll see that
the wait_for() method checks the ready sets first and doesn't block if
there are any bits set in those masks. This features makes it
possible for Event_Handlers to control subsequent dispatching policies
of the Reactor.
Meaning of return value from ACE_Event_Handler
callback.
What do return values from an ACE Event_Handler callback (e.g.,
handle_input()/handle_output do?
Note that the return values of 0 and < 0 are by far the most common
cases -- > 0 is only used for unusual circumstances.
X Windows select blocking and ACE_Reactor.
If I let X do the select blocking, will that have any affect on
the Reactor performing signal handling?
Yes, I think that will cause problems since the Reactor relies
on a "handshake" between its Signal_Handler component and its
handle_events loop to properly handle signals.
Poll vs Select
Is the Poll method preferred over Select if it is available - why?
For systems that implement select() in terms of poll() (e.g., Solaris 2.x)
then it may be somewhat faster. Otherwise, it doesn't really
matter since (1) they (should) do the same thing and (2) the end user
shouldn't notice any change in behavior.
Disk space required for ACE.
I would very much like to evaluate/use the ACE Toolkit,
but am limited as to disk space on our system.
What is the total disk space required for a compiled,
usable toolkit?
The source code is around 6 Meg (uncompressed) and 1 Meg (compressed).
The amount of disk space required to build ACE depends entirely on the
C++ compiler and OS platform you use. In general, if you just want to
use the ACE libraries, you don't have to build anything other than the
$ACE_ROOT/ace/ directory.
Size of and speed of executable linked to
ACE library.
This is regarding the newer release of ACE and pertaining to the library
archive file. My question is, if all the ".o" files are archived into one
single "libACE.a", does it increase the size of the executable
program?
No. The use of a *.a file allows the linker to extract out only those
*.o files that are actually used by the program.
If it does, then does a large executable program mean possibility of it being
slower?
No.
Multiple reactors per process or thread.
What happens if I have several reactors in a process (e.g. in different
threads)?
Naturally, the behavior of this all depends on the semantics
of the threads package... In Solaris 2.x, signal handlers are shared
by all threads. Moreover, the Reactor uses a static table to hold the
thread handlers. Thus, only one of the handlers would be registered
(i.e., whichever one was registered second).
Programmer 3 designs the process and decides to have thread 1 and thread 2
running in the same process and also makes use of a third party software library
that internally has also registered a signal handler (not at the reactor) for
SIGUSR.
Now you've got big problems! This is an example of a
limitation with UNIX signal handlers... In general, it's a bad idea
to use signal handlers if you can avoid it. This is yet another
reason why.
When looking into
$ACE_ROOT/tests/Reactor/misc/signal_tester.C you have shown a way
to do this by marking the dummy file_descriptor of the Sig_Handler
object ready for reading asynchronously. The handle_input() routine of
Sig_Handler object will then be dispatched synchronously. But what
happens if I have several reactors. The asynchronously dispatched
handle_signal() routine does not know via which reactor it has been
registered so in which reactor to modify the dummy file_descriptor.
Is your suggestion to have just one process global reactor in such a
case?
Yes, precisely. If you are going to be handling signals, I
would recommend against using several reactors within separate threads
within the same process. Can you use 1 reactor and/or have one
reactor handle signals within a process?
One thing we want to do is the priorization of Event_Handlers, i.e.,
in case of concurrent events the sequence in which the Event_Handler
methods will be activated depends on their priority relative to each
other. We have two choices:
Right now I would think that we have to use the second choice if we want to
use the feature of asynchronous output with automatic re-queueing.
Am I right?
Hum, that's an interesting problem. It might be better to
subclass the Reactor to form a new class called Priority_Reactor.
This subclass would override the Reactor's dispatch method and
dispatch the event handlers in "priority" order. We're in the process
of doing this for Siemens. The results should be available soon.
Non-CORBA version.
Is the non-CORBA version still around? I think I still need it to
get around certain link error, or is something else?
There are two ways to get around this problem:
Recursive Mutex Locks.
We are using your ACE software and ran into a problem which may
or may not be related to the mutex locks. The question may have more
to do with how mutex locks should be used. We had a class which was
using your mutex lock wrapper. Each member function of the class
acquired the lock before processing and released on exiting the
function. Some member functions may call other member functions. The
following is an example:
Ah, that's a great question since there are subtle and
pernicious problems lurking in the approach you are trying above.
Basically, Solaris mutex locks are *not* recursive (don't ask why...)
Thus, if you want to design an application like the one above you'll
need to use one or more of the following patterns:
I'd be interested to hear any comments on these approaches.
Non-blocking socket connections.
I am working on Solaris 2.3 and trying to understand how to get
around the problem of trying to open a Socket connection to a remote
host that is "dead". Of course you get a nice long process block if
the socket is in Blocking mode (TCP lets you know when you can
continue - how polite).
So how does a non-blocking connect work with respect to using
the Reactor and a SOCK_Stream object to coordinate the opening
of the connection? Do I wait on the OUTPUT event for the FD?
How do I know if the connect worked or possibly timed-out? Is
this a reliable approach (I read somewhere that this will only
work if the STREAMS module is at the top of the protocol stack
- MAN page I think)?
An example of implementing this is in the Gateway sample application
in the new ACE. It's also encapsulated in the Connector<> pattern of
the Connection class category in ./libsrc/Connection. You may want to
take a look at those two things for concrete usage examples.
However, the basics of getting non-blocking to work are the following:
Is using a separate thread to make the connection a better way to avoid
the potentially long block in the main thread during the connect call?
You could do that, but it can all be accomplished in a single process using
the facilities available.
Reactor scheduling priorities.
I was wondering, does the Reactor class have the ability to prioritize
activity on the registered event handlers?
The default strategy for the Reactor's dispatch routine
(Reactor::dispatch) does not prioritize dispatching other than to
dispatch callbacks in ascending order from 0 -> maxhandlep1.
We have a requirement to be able to process both real-time, as well as, stored
telemetry and ERMs concurrently. Real-time needs to be processed at a higher
priority than stored data. Our design is based on both real-time and stored
data coming into our process via separate sockets.
There are several ways to do this:
Sun MP bug and non-blocking connects in
Gateway application.
I compiled the new ACE 3.2.0 's apps/Gateway. The compiling went
through without any errors. But I could not get it running, neither single
threaded nor multi-threaded. The cc_config and rt_config files entries are given
below. Also the machine configurations are given below. Does it need some more
settings or some patch !!??
I believe you are seeing the effects of the dreaded Sun MP bug
with non-blocking connects. The easy work around for now is simply to
give the "-b" option to the Gateway::init() routine via the svc.conf
file:
Removal of dynamically loaded objects from
Service_Config.
When are dynamically loaded objects removed from the Service_Config.
The Service Configurator calls dlclose() when a "remove Service_Name"
directive is encountered in the svc.conf file (or programmatically
when the Service_Config::remove() method is invoked). Check out the
code in ./libsrc/Service_Config/Service_Repository.i and
./libsrc/Service_Config/Service_Config.i to see exactly what happens.
Forking vs threading by Service
Configurator.
In the Service Configurator, when an item is entered in the svc.conf
how do you know which items will be invoked as threads and
which items are forked. I know that static items are executed
internally.
No! It's totally up to the subclass of Service_Object to
decide whether threading/forking/single-threading is used. Check out
the ./apps/Logger/Service_Configurator_Logger for examples of
single-threaded and multi-threaded configuration.
Cleanup of new objects in Service Configurator
Logger.
I have been reading the Service Configurator Logger. I was wondering about
cleanup of new objects. In the handle_input method for the Acceptor a new
svc_handler is allocated for each new input request and deleted in the
handle_close. I was wondering how handle close was called when a client who
has created a socket terminates the connection (i.e., when is handle_close
called).
handle_close() is automatically called by the Reactor when a
handle_input()/handle_output()/etc. method returns -1. This is the
"hook" that instructs the Reactor to call handle_close() and then
remove the Event_Handler object from its internal tables.
Removal of client socket in Logger upon termination
of client.
How does the Logger know to remove the client socket and the svc_handler object.
Does he receive an exception.
No. when the client terminates the underlying TCP/IP
implementation sends a RESET message to the logger host. This is
delivered to the logger process as a 0-sized read(). It then knows to
close down.
What I am worried about is a leak. Where by a lot of clients connect and
disconnect and the server does not cleanup correctly. Such as a core dump
from the client where he cannot close correctly.
That's handled by the underlying TCP (assuming it is
implemented correctly...).
What I am doing is attempting to convert the logger example into an alarm
manager for remote nodes. In this application a node may be powered down
there by terminating a Logger/Alarm server connection abnormally, this could
leave the Logger with many dangling sockets and allocated svc_handler
objects.
If the TCP implementation doesn't handle this correctly then
the standard way of dealing with it is to have an Event_Handler use a
watchdog timer to periodically "poll" the client to make sure it is
still connected. BTW, PCs tend to have more problems with this than
UNIX boxes since when they are turned off the TCP implementation may
not be able to send a RESET...
Using templates with Centerline.
Centerline uses ptlink to process the C++ templates. ptlink expect the
template declarations and definitions (app.h and app.C) to reside in
the same directory. This works fine for the ACE hierarchy since
everything is a link to the appropriate src directory (include/*.[hi]
--> ../src/). When a users of the ACE distribution attempts to include
the ACE classes in an existing application hierarchy this problem will
arise if ptlink is used.
The solution is to create a link to the declaration file from the
definition file directory and use the "-I" to point to the definition
directory.
Bug with SUNPro CC 4.0 and -g option.
When I try to compile $ACE_ROOT/src/Message_Queue.C on a Solaris 5.3
system using SUNPro CC 4.0, the compiler aborts with a Signal 10
(Bus Error). Our copy of CC 4.0 is over a year old and I do not
know if any patches or upgrades exist for it. If they do, then we
have not applied them to our compiler.
Several other people have run across this as well. It turns
out that there is a bug in the Sun 4.0.0 C++ compiler that will get a
bus error when -g is used. If you compile Message_Queue.C *without*
-g then it works fine. The later versions of SunC++ don't have this
bug. I'd recommend that you upgrade as soon as possible.
Mixing shared and non-shared objects with Service Configurator.
I have added a dynamic service to the Service Configurator. This new service
fails on the load because it uses application libraries that are not shared
object libraries (i.e., objects in libApp.a). I am assuming from the error
message that the problem is the mix match of shared and non-shared
objects.
Right, exactly.
I was wondering if there is an easy way to add static services to the
Service Configurator. The example directory listing static service is
very tightly coupled with the Service_Config object. Is there another
way of adding static services.
Sure, that's easy. The best way to do this is to use the
interfaces of the Service_Respository class to configure static
services into the Service_Config. A good example of how to do this is
in Service_Config.[Chi]:
Examples of the SYNC/ASYNC pattern.
Do you have examples of the SYNC/ASYNC pattern?
Yes. Check out the following:
Using delete on "this".
We had a discussion about something we saw in the new ACE code.
I thing there was a member function of a class that was doing a
"delete this". Is this safe?
In general it is safe as long as (1) the object has been allocated
dynamically off the heap and (2) you don't try to access the object
after it has been deleted. You'll note that I tend to use this idiom
in places where an object is registered with the Reactor, which must
then must ensure the object cleans itself up when handle_close() is
called. Note that to ensure (1) I try to declare the destructor
"private" or "protected" so that the object must be allocated off the
heap (some compilers have a problem with this, so I may not be as
consistent as I ought to...).
Correct way for building a modified ACE
library.
What is the correct way for building a modified ACE library?
Changing in "libsrc" or in "include" directory?
When I make a complete new directory, how can I get introduced
the dependencies within my new makefile, can you give a short hint?
Sure, no problem. For instance, here's what I did tonight when I
added the new Thread_Specific.[hiC] files to ACE:
% ln -s ../../libsrc/Threads/Thread_Specific.[hi] .
% ln -s ../../libsrc/Threads/Thread_Specific.C .
% make depend
on the ./src directory, which updated the dependencies.
Problems with HP/UX.
The following is from Neil B. Cohen (nbc@metsci.com), who is
writing about how to work around problems he's found with HP/UX.
I've been trying to compile the latest beta (3.2.9) on an HP running
HPUX9.05 for the past week or so. I've had problems with templates up
and down the line. I finally discovered (after some discussions with
the HP support people) that they have made numerous changes to their
C++ compiler recently to fix problems with templates and
exceptions. If you are trying to compile ACE under HPUX with anything
less than version 3.70 of the HP compiler, you may have serious
problems (we were using v3.50 which came with the machine when we
bought it a few months ago).
Also, unlike earlier ACE versions, I was forced to add the following
line to the rules.lib.GNU file to "close" the library - ie. force the
various template files to be instantiated and linked to the ACE
library itself. I don't know if this is necessary, or the only way to
make things work, but it seems to do the job for my system.
in rules.lib.GNU...
Memory leaks reported in ACE by Purify.
I just ran my program with Purify, and it is telling me that there
is at least one large (~4k) memory leak in
ACE_Thread_Specific<ACE_Log_Msg> This may or may not be serious,
but it is probably worth looking into.
Right, that's ok. This is data that's allocated on a "per-thread"
basis the first time a thread makes a call using the LM_ERROR or
LM_DEBUG macros. The data isn't freed-up until the thread exits.
Static vs dynamic binding for IPC_SAP.
In my trying to use the Reactor pattern for my application I
noticed that I had to couple my eventHandler derived objects with a
specific IPC_SAP mechanism. To use some of your own examples your
Client_Stream object contains a TLI_Stream object to use in data
transfer. My application calls for determining the communication
mechanism at run time. To do this my eventHandler must be able to
create the appropriate IPC_Stream object at run time and use its
methods through a super class casting. The problem is that there is no
super class with the virtual methods for send, recv, etc. To solve my
problem I will create that super class and have the TLI ( as well as
other wrapper objects) inherit from it instead of IPC_SAP. My question
is I am suspicious of why ACE wasn't designed with that in mind? Is my
application that unique ? or is there a better way to do this that I
am not aware of ? Your help in this matter will be much appreciated.
ACE was developed using static binding for IPC_SAP in order to
emphasize speed of execution over dynamic flexibility *in the core
infrastructure*. To do otherwise would have penalized the performance
of *all* applications in order to handle the relatively infrequent
case where you want to be able to swap mechanisms at run-time.
Since it is straightforward to create an abstract class like the one
you describe above I decided to make this a "layered" service rather
than use this mechanism in the core of ACE.
BTW, I would not modify TLI_SAP and SOCK_SAP to inherit from a new
class. Instead, I would use the Bridge and Adapter patterns from the
"Gang of Four" patterns catalog and do something like this:
Obtaining ACE documentation.
I think that your site is cool, but it's being a terrible tease
in that I really want to read the contents, but don't know anything
about x-gzip formatting. I'm running Netscape 2.0 under MS Windows
NT.
x-gzip is a Netscape hook for the GNU "gzip" program, which is
freely available for NT from prep.ai.mit.edu in the
/pub/gnu/ directory. Here's how our "Global Mailcap" entry for
Netscape looks like (see the "Helper Applications" menu under
"preferences"):
More information related to postscript and gzip are available online.
Static instance of Reactor.
We would like to use the Reactor class as a static member on some of
our classes (one per process) so that we can see and use the Reactor
within each process on a global level. We are using it to set
timers several levels down in our class trees and don't want to pass
a pointer to it through all of our constructors. My question is:
are there any static initialization dependencies that you know of
when using the default "do nothing" constructor of the Reactor that
could prevent use from using it as a static member variable? Thanks
for any advice on this issue.
The only problems you'll have are the typical ones about "order of
initialization" of statics in separate files. You'll also have to
live with the default size of the I/O handler table, which probably
isn't a problem since the max is something like 1024 or so.
BTW, I solve this problem in ACE via the Service_Config::reactor(),
which uses the Singleton pattern to generate a single static pointer
to a Reactor that can be used process-wide.
ACE-3.3, HP-UX, and cloning.
I just got the ACE-3.3 version and am trying it on the HP-UX.
I run into a small problem while cloning the directories that
might be worth fixing.
I made a directory called ACE_WRAPPERS/HP-UXA.09.05-g1, cd to it
and run "make -f ../Makefile clone". when I look in src, I have:
Acceptor.C@ -> ../libsrc/Connection/Acceptor.C
However, ../libsrc does not exist. It is not one of the CLONE
variables in ACE_WRAPPERS/Makefile. I don't think you'd want to
clone libsrc too, since its files don't change.
I think you can solve this problem as follows:
How long and on what projects has ACE been used
industrially?
Our quality personal has asked me the following questions for which
I think you are the right guy for answering that:
How long is ACE used in industrial products?
It was first used at Ericsson starting in the fall of 1992, so that
makes it about 6 years now.
What are reference projects comparable to ours that use ACE?
Some of the ones I have directly worked with include:
How many people have contributed to ACE testing
and error reports?
Over 600. All the contributors are listed by name and email address
at the end of the README
file distributed with the ACE release.
How many bug fixes have been made since ACE was
public domain?
All information related to bug fixes for the past 7 years is available
in the ChangeLog files distributed with the ACE release.
How much literature is there on ACE?
All articles published about ACE are referenced in the BIBLIOGRAPHY
file in the top-level directory of ACE.
Papers on benefits and problems of C++
wrappers.
We are currently evaluating ACE for use on a new telecom switch.
Many of us like ACE but are having trouble convincing some team
members that wrappers are better than using the direct Unix
system calls.
I have read your papers that came with ACE, but was wondering if there
are other papers that address the benefits (or problems) of
wrappers?
This topic has been discussed in other places, most notably the book
by Erich Gamma and Richard Helm and Ralph Johnson and John Vlissides
called "Design Patterns: Elements of Reusable Object-Oriented
Software" (Addison-Wesley, 1994), where it is described in terms of
the "Adapter" pattern.
Very briefly, there are several key reasons why you should *not* use
UNIX system calls directly (regardless of whether you use ACE or not).
Unless you plan to develop code on only 1 UNIX platform (and
you never plan to upgrade from that platform as it goes
through new releases of the OS) you'll run across many, many
non-portable features. It's beyond the scope of this
FAQ to name them all, but just take a look at ACE sometime
and you'll see all the #ifdefs I've had to add to deal with
non-compatible OSs and compilers. Most of these are centralized
in one place in ACE (in the ace/OS.*files), but it took a lot
of work to factor this out. By using wrappers, you can avoid
most of this problem in the bulk of your application code
and avoid revisiting all of these issues yourself.
In addition, ACE is now ported to other platforms (e.g.,
Windows NT and Windows 95). If you want to write code that
is portable across platforms, wrappers are a good way to
accomplish this.
I'd go as far as to say that anyone who wants to program
applications using C-level APIs like sockets or TLI is not
serious about developing industrial strength, robust, and easy
to maintain software. Sockets and TLI are incredibly
error-prone and tedious to use, in addition to being
inherently non-portable. I've got a paper that discusses this
in detail at the following WWW URL: http://www.cs.wm.edu/~schmidt/PDF/COOTS-95.pdf.
Here's where the Adapter pattern stuff really pays
off. For example, by making all the UNIX network
programming interfaces and synchronization mechanisms
have the same API I can write very powerful higher-level
patterns (e.g., Connector and Acceptor) that generalize
over these mechanisms. For proof of this, take a look
at the ./tests/Connection/non_blocking directory
in the latest ACE-beta.tar.gz at wuarchive.wustl.edu
in the /languages/c++/ACE directory. It implements
the same exact program that can be parameterized
with sockets, TLI, and STREAM pipes *without*
modifying any application source code. It is
literally impossible to do this without wrappers.
Reactor thread detection of new data in shared
memory.
How can I use a kind of "Reactor" in such a way that a reading
thread can notice the arrival of new data on several shared memory
areas?
Ah, that is a tricky issue! The underlying problem is that UNIX is
inconsistent with respect to the ability to "wait" on different
sources of events. In this case, Windows NT is much more consistent
(but it has its own set of problems...).
Poll, Select and Reactor (so far I read) assume that file
descriptors are present, which is not the case with shared memory.
That's correct (though to be more precise, the Reactor can also deal
with signals, as I discuss below).
Is there a common and efficient way to deal with that kind of
situation, or do I have to insert extra ipc mechanisms (based on
descriptors)?
There are several solutions:
C++ streams for "communication
methodologies".
What do you think about wrapping communication methodologies in C++ streams?
What I mean is having defining a stream and extractor/insertor functions
which the underlying implementation reads/writes on comm mechanisms instead of
files. I would think this to be a very general interface for all comms
implementations. All user code would look the same, but the underlying stream
implementations would be different. Whether the stream functionality would
be defined by the stream itself (eg tcpstream) or with manipulators
(eg commstream cs; cs << tcp;) is up for grabs in my mind.
Anyhow, I was wondering your input...
That technique has been used for a long time. In fact, there are
several freely available versions of iostreams that do this and
RogueWave also sells a new product (Net.h++) that does this. I think
this approach is fine for simple applications.
However, it doesn't really work well if you need to write
sophisticated distributed applications that must use features like
non-blocking I/O, concurrency, or that must be highly robust against
the types of errors that occur in a distributed system.
For these kinds of systems you either need some type of CORBA ORB, or
you need to write the apps with lower-level C++ wrappers like the ones
provided by ACE.
cont() vs. next() in ACE_Message_Block.
What is the difference between cont() and next() in
ACE_Message_Block?
Ah, good question. cont() gives you a pointer to the next
Message_Block in a chain of Message_Block fragments that all belong to
the same logical message. In contrast, next() (and prev()) return
pointers to the next (and previous) Message_Block in the doubly linked
list of Message_Blocks on a Message_Queue.
BTW, this is exactly the same structure as in System V Streams...
Which would I use if I wanted to add a header and a trailer, each stored in
ACE_Message_Blocks of their own, to another ACE_Message_Block?
You should use cont() for that.
Getting a port number.
I'm trying to do the following:
Is there I way to get the port number that I have missed?
Sure, can't you just do this:
ACE inlining policies.
I have seen 2 different inlining policies in ACE. The first is
that the .i file is included unconditionally by both the .h and .C
file and all functions in the .i file carry the "inline" keyword.
Right. Those are for cases where I always want to inline
those methods. I do this mostly for very short wrapper methods (e.g.,
read() or write()) that are likely to be on the "fast path" of an
application.
The second policy is where the .i file is included by the .h file
ONLY if __INLINE__ is defined for the compile. This causes the
functions in the .i file to be compiled as inline functions (INLINE
translates to inline in this case). If __INLINE__ is not defined, the
.i file is only included by the .C file and the functions do NOT carry
the "inline" keyword.
I do this for cases where it's really not essential to have those
methods inline, but some users might want to compile ACE that was if
they want to eliminate all the wrapper function-call overhead. For
instance, I'll typically do this when I'm running benchmarks.
Integrating ACE and CORBA.
Our goal is to implement a CORBA-II compliant application. I am
trying to conceptually visualize the applicability to ACE to this
attempt (which we're pretty excited about), and I was hoping you'd
offer any opinions / observations that you might have.
We've successfully integrated ACE with several implementations of
CORBA (in particular Orbix 1.3 and 2.0) and used it in a number of
commercial applications. In these systems, we use ACE for a number of
tasks, including the following:
We've written a technical
paper for the USENIX COOTS
conference that describes more info about the ACE/CORBA
integration and the performance issues associated with it.
Calling Reactor::handle_events() recursively.
Can the Reactor's event loop be called recursively?
The ACE_Select_Reactor and ACE_TP_Reactor's event loops can be called
recursively. However, The ACE_WFMO_Reactor's event loop is not
reentrant (though it is thread-safe) since it maintains state about
the active descriptors it is iterating over. Therefore, depending on
the descriptors you're selecting on, you could end up with spurious
handle_*() callbacks if you make nested calls to the
ACE_WFMO_Reactor::handle_events() method.
For example, if I have a program that sets up some event handlers
and then calls, in an infinite loop, ACE_Reactor::handle_events().
Can one of the event handlers call handle_events() again if it needs
to block, while allowing other event handlers a chance to run?
Yes, as long as it's an ACE_Select_Reactor or ACE_TP_Reactor.
However, when you returned back from the nested call to
handle_events() you'll need to write your event handler's carefully to
know what state you were in and how to proceed.
BTW, here's one way to design single-threaded reactive applications to
avoid recursive event loops:
Integrating the Reactor with SysV Message Queues.
In one of my programs, a process needs to receive input from multiple
input sources. One of the input sources is a file descriptor while
another is a message queue. Is there a clean way to integrate this a
message queue source into the Reactor class so that both inputs are
handled uniformly?
Do you have multiple threads on your platform? If not, then life will
be *very* tough and you'll basically have to use multiple processes to
do what you're trying to do. There is *no* portable way to combine
System V message queues and file descriptors on UNIX, unfortunately.
If you do have threads, the easiest thing to do is to have a thread
reading the message queue and redirecting the messages into the
Reactor via its notify() method.
Please take a look at the program called
examples/Reactor/Misc/notification.cpp
for an example of how to do this.
Determining the localhost name.
I'm writing a program to find out the address for a socket. The
idea is that we open an ACE_Acceptor (and will eventually perform
accept() on it.) Before we can do that we need to find out the
address of the ACE_Acceptor so that we can publish it (for others to
be able to connect to it.) The trouble is that the call
ACE_INET_Addr::get_host_name () prints "localhost" as the host name
while I would like to principal host name to be printed instead.
All ACE_INET_Addr::get_host_name() is doing is calling
ACE_OS::gethostbyaddr(), which in turn will call the socket
gethostbyaddr() function. I suspect that what you should do is
something like the following:
Non-blocking socket I/O.
Could you please point me to stuff dealing with asynchronous
cross platform socket calls. I want to use non blocking socket calls
on both UNIX and NT.
Sure, no problem. Take a look at the
Exceptions and the Reactor.
Is ACE exception-safe? If I throw an exception out of event
handler, will the Reactor code clean itself?
Yes, that should be ok. In general, the two things to watch out for
with exceptions are
Customizing Shared Memory Keys for ACE_Malloc.
I am building a Shared memory manager object using MMAP and MALLOC
basically as:
Yes, that is correct. That design decision was made to keep a clean
interface that will work for all the various types of memory pools.
Is there any recommended way to specialize ACE classes to allow this
key to be overridden?
Yes indeed, you just create a new subclass (e.g., class
My_Memory_Pool) that inherits from ACE_MMAP_Memory_Pool and then you
pass in the appropriate key to the constructor of ACE_MMAP_Memory_Pool
in the constructor of My_Memory_Pool. Then you just say:
ACE Tracing.
What is the best way to turn on TRACE output in ACE. I commented
out the #define ACE_NTRACE 1 in config.h and rebuilt ACE and the
examples.
The best way to do this is to say:
When I run the CPP-inserver example in examples/IPC_SAP/SOCK_SAP, I
get some trace output but not everything I would expect to see.
Can you please let me know what you'd expect to see that you're not
seeing? Some of the ACE_TRACE macros for the lower-level ACE methods
are commented out to avoid problems with infinite recursion (i.e.,
tracing the ACE_Trace calls...). I haven't had a chance to go over
all of these indepth, but I know that it should be possible to turn
many of them back on.
It would be nice to have a runtime option for turning trace on and off.
There already is. In fact, there are two ways to do it.
If you want to control tracing for the entire process, please check
out ACE_Trace::start_tracing() and ACE_Trace::stop_tracing().
If you want to control tracing on a per-thread basis please take a
look at the ACE_Log_Msg class. There are methods called
stop_tracing() and start_tracing() that do what you want.
ACE Signal Handling.
I've been using an acceptor and a connector in one (OS-) process.
What does happen, if a signal is sent to this process? Is the signal
processed by every ACE_Event_Handler (or its descendants) that is
around? The manual page simply states that handle signal is called
as soon as a signal is triggered by the OS.
How this signal is handled depends on several factors:
$ACE_ROOT/examples/Reactor/Misc/test_signals.cpp
This contains a long comment that explains precisely how everything
works!
Y2K Compliance.
Is ACE Y2K compliant?
Yes, ACE is fully Y2K compliant, as long as the underlying OS is Y2K
compliant! The only place that ACE uses "dates" is in its timer queue
mechanism, where is uses 32 (or 64) bit quantities. So it should
have no Y2K problems.
Back to the ACE home page.
Service_Config (int ignore_defaults = 0,
size_t size = Service_Config::MAX_SERVICES,
int signum = SIGHUP);
No matter how many times I used ranlib or removed the libraries and re-compiled
or whatever. Perhaps these are System V specific and will not work on 4.1.3?
ld: libASX.a: warning: archive has no table of c ontents;
add one using ranlib(1)
* Sun OS 4.1.x
Note that on SunOS 4.x you may get warnings from the
linker that "archive has no table of contents; add
one using ranlib(1)" for certain libraries (e.g.,
libASX.a, libThreads.a, and libSPIPE.a). This
occurs since SunOS 4.x does not support these features.
never able to get .so -- assume these are shared libraries that gcc can not
deal with.
sa.sa_handler = SIG_DFL;
The problem is that most versions of UNIX I've come across don't have
a correct prototype for this field of struct sigaction. That's why I
define two variants of signal handler typedefs: one that is a typedef
of the "correct version" (which I call SignalHandler) and one of which
is a typedef of the "incorrect version" (which I call SignalHandlerV).
You might check out the sysincludes.h file to see how it is defining
SignalHandlerV and make sure this matches what your OS/Compiler
defines in <sys/signal.h>.
BUILD = $(VLIB) $(VSHLIB) $(SHLIBA)
to
BUILD = $(VSHLIB) $(SHLIBA)
Where signal_catcher is like this:
pthread_create(&tid, thread_attr, signal_catcher, NULL);
pthread_detach(&tid);
static void *
signal_catcher(void *arg)
{
static int catch_sigs[] = {
SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGCHLD
};
sigset_t catch_these;
int i;
error_status_t st;
for ( ; ; ) {
sigemptyset(&catch_these);
for (i = 0; i < sizeof catch_sigs / sizeof catch_sigs[0]; i++)
sigaddset(&catch_these, catch_sigs[i]);
i = sigwait(&catch_these);
/* Note continue below, to re-do the loop. */
switch (i) {
default:
fprintf(stderr, "Caught signal %d. Exiting.\n", i);
CLEANUP_AND_EXIT();
/* NOTREACHED */
#if defined(SIGCHLD)
case SIGCHLD:
srvrexec__reap();
continue;
#endif /* defined(SIGCHLD) */
}
}
return NULL;
}
dynamic Remote_Brdcast Service_Object * .shobj/Handle_Broadcast.so:remote_broadcast "-p 10001"
jupiter[12] %server_test -d
starting up daemon server_test
opening static service Svc_Manager
did static on Svc_Manager, error = 0
signal signal 1 occurred
beginning reconfiguration at Sat Feb 25 13:40:29 1995
Segmentation fault (core dumped)
Programmer 1 decides to register at reactor 1 in his thread 1 a signal handler
for SIGUSR.
Programmer 2 decides to register at reactor 2 in his thread 2 a signal handler
for SIGUSR.
1. Set your ORBIX_ROOT environment variable to the location of the
Orbix release (e.g., /opt/Orbix). Naturally, this only works
if you've got Orbix installed on your machine.
2. If you don't have Orbix, then to get rid of that problem all you
need to do is change the symbolic links on the
./include/config.h
./include/makeinclude/platform_macros.GNU
files to
./include/config-sunos5-sunc++-4.x
./include/makeinclude/platform_sunos5_sunc++.GNU
rather than the *-orbix* versions. B default, ACE is configured
not to use CORBA.
Is this valid ? My assumption is that the mutex lock is recursive and
the same thread can acquire the lock multiple times in different
member functions.
class foo {
void a() {
MT( Mutex_Block<Mutex> m( this->lock_ ));
if( cond )
b();
}
void b() {
MT( Mutex_Block<Mutex> m( this->lock_ ));
if( cond )
a();
}
};
BTW, all three of these patterns are used in the ACE Reactor
class category. The Reactor has a number of fairly complex
concurrency control and callback issues it must deal with and I've
found it useful to use all three of these patterns jointly.
The advantage with this is that it requires almost no
changes to existing code. The disadvantage is that
recursive locks are just slightly more expensive.
class Foo
{
public:
void a()
{
MT( Guard<Recursive_Lock <Mutex> > m( this->lock_ ));
b ();
}
void b()
{
MT( Guard<Recursive_Lock <Mutex> > m( this->lock_ ));
b_i ();
}
};
The advantage here is that inline functions can basically
remove all performance overhead. The disadvantage is that
you need to maintain two sets of interfaces.
class Foo
{
public:
void b()
{
MT( Guard<Mutex> m( this->lock_ ));
b_i ();
}
void b_i()
{
if( cond )
a_i();
}
void a_i()
{
if( cond )
b_i();
}
void a()
{
MT( Guard<Mutex> m( this->lock_ ));
a_i ();
}
};
The disadvantage with this, of course, is that you
greatly increase your locking overhead. In addition,
you need to be very careful about introducing race
conditions into the code. The primary reason for
using this approach is if you need to call back to
code that you don't have any control over (such as
OS I/O routines) and you don't want to hold the
lock for an indefinite period of time.
class Foo
{
public:
void b()
{
MT( Guard<Mutex> m( this->lock_ ));
m.release ();
a ();
m.acquire ();
}
void a()
{
MT( Guard<Mutex> m( this->lock_ ));
m.release ();
b ();
m.acquire ();
}
};
When an event is returned
If you want to "time out" after a certain period of time, consider
registering for a timer event with Reactor. If the timer goes off before
the connection succeeds, close down the appropriate socket.
BTW, I'm not sure what you mean by "real-time" but I assume that you
are aware that there is no true "real-time" scheduling for network I/O
in Solaris. However, if by "real-time" you mean "higher priority"
then either of the above strategies should work fine.
dynamic Gateway Service_Object *.shobj/Gateway.so:_alloc_gatewayd() active
"-b -d -c cc_config -f rt_config"
If you check line 137 of the Gateway::parse_args() method you'll see
what this does.
int
Service_Config::load_defaults (void)
{
for (Static_Svc_Descriptor *sl = Service_Config::service_list_;
sl->name_ != 0; sl++)
{
Service_Type *stp = ace_create_service_type (sl->name_, sl->type_,
(const void *) (*sl->alloc_)(),
sl->flags_);
if (stp == 0)
continue;
const Service_Record *sr = new Service_Record (sl->name_, stp, 0,
sl->active_);
if (Service_Config::svc_rep->insert (sr) == -1)
return -1;
}
return 0;
}
$(VLIB): $(VOBJS)
- CC -pts -pth -ptb -ptv -I$(ACE_ROOT)/include $(VOBJS)
$(AR) $(ARFLAGS) $@ $? ./ptrepository/*.o
-$(RANLIB) $@
-chmod a+r $@
I added the CC line, and added the "./ptrepository/*.o" to the $(AR)
cmd. Sun has an -xar option, I believe that does something similar to
this. Also - note that I'm not sure that the "-ptb" option is
necessary. I added that before we upgraded the compiler, so it may not
be needed now...
// Abstract base class
class ACE_IPC_Stream
{
public:
virtual ssize_t recv (void *buf, size_t bytes) = 0;
virtual ssize_t send (const void *buf, size_t bytes) = 0;
virtual ACE_HANDLE get_handle (void) const = 0;
// ...
};
and then create new classes like
template <class IPC>
class ACE_IPC_Stream_T : public ACE_IPC_Stream
{
public:
virtual ssize_t recv (void *buf, size_t bytes)
{
return this->ipc_.recv (buf, bytes);
}
virtual ssize_t send (const void *buf, size_t bytes)
{
return this->ipc_.send (buf, bytes);
}
virtual ACE_HANDLE get_handle (void)
{
return this->ipc_.get_handle ();
}
// ...
private:
IPC ipc_;
// Target of delegation
// (e.g., ACE_SOCK_Stream or ACE_TLI_Stream).
}
Then you could write code that operated on ACE_SAP *'s to get a
generic interface, but that reused existing code like SOCK_SAP and
TLI_SAP, e.g.,
class My_Event_Handler : public ACE_Event_Handler
{
public:
My_Event_Handler (void) {
// Figure out which IPC mechanism to use somehow:
if (use_tli)
this->my_ipc_ = new ACE_SAP_IPC<ACE_TLI_Stream>
else if (use_sockets)
this->my_ipc_ = new ACE_SAP_IPC<ACE_SOCK_Stream>
else
...
}
private:
ACE_IPC_Stream *my_ipc_;
};
There are obviously details left out here, but this is the general idea.
----------------------------------------
# For the format of this file, see
# ftp://wuarchive/doc/internet-drafts/draft-borenstein-mailcap-00.txt.gz
audio/*; audiotool %s; test=test -n "$DISPLAY" && test -w /dev/audio
image/*; xv %s; test=test -n "$DISPLAY"
application/postscript; ghostview %s; test=test -n "$DISPLAY"
video/mpeg; mpeg_play %s; test=test -n "$DISPLAY"
video/*; xanim +Ae %s; test=test -n "$DISPLAY"
application/x-dvi; xdvi %s; test=test -n "$DISPLAY"
application/x-compress; uncompress %s; test=test -n "$DISPLAY"
application/x-gzip; gunzip %s; test=test -n "$DISPLAY"
application/x-zip; unzip %s; test=test -n "$DISPLAY"
----------------------------------------
BTW, if you can't get gzip to work via Netscape, please ftp to
wuarchive.wustl.edu and look in the directory
/languages/c++/ACE/ACE-documentation/. All the papers are there,
as well, compressed with the GNU gzip program.
% cd ACE_WRAPPERS
% setenv ACE_ROOT $cwd
% cd HP-UXA.09.05-g1
% make -f ../Makefile clone
% setenv ACE_ROOT $cwd
% make
That should build the links correctly since they'll point to the
absolute, rather than relative, pathnames!
In addition, there are probably about 200-300 or more other companies
that have used ACE in commercial products. The current mailing list
has about 1,000 people from about 500 different companies and
universities. A description of how some companies are using ACE is
available online.
It looks like I need "ACE::bind_port" to return the port number that
it picked and "ACE_SOCK_Dgram::shared_open" will need it store the
port number so I could call some function like
ACE_SOCK_Dgram::get_port_number or it would need to return the port
number instead of the handle(I could always call
ACE_SOCK_Dgram::get_handle if I needed the handle).
// Defaults to all "zeros", so bind will pick port.
ACE_INET_Addr dg_addr;
ACE_SOCK_Dgram dg;
dg.open (dg_addr);
dg.get_local_addr (dg_addr);
dg_addr.get_port_number ();
plus many more.
There are a number of places to find more information on this sort of
design:
ACE_Acceptor listener (ACE_Addr::sap_any);
ACE_INET_Addr addr;
listener.get_local_addr (addr);
char *host = addr.get_host_name ();
if (::strcmp (host, "localhost") == 0)
{
char name[MAXHOSTNAMELEN];
ACE_OS::hostname (name, sizeof name);
cerr << name << endl;
}
else
cerr << host << endl;
directory. There are a number of examples there. In addition, there
are examples of non-blocking connections in
./examples/Connection/non_blocking/
The code that actually enables the non-blocking socket I/O is in
ace/IPC_SAP.cpp.
./examples/IPC_SAP/SOCK_SAP/CPP-inclient.cpp
I noticed that the ACE_MMAP_Memory_Pool class provides for the
users to specify a Semaphore key. However, once I use it via the
ACE_Malloc<..>::ACE_Malloc(const char* poolname) constructor, I
lose this option.
typedef ACE_Malloc<ACE_MMAP_Memory_Pool, ACE_Process_Mutex> SHMALLOC;
Please check out the file:
typedef ACE_Malloc<My_Memory_Pool, ACE_Process_Mutex> SHMALLOC;
examples/Shared_Malloc/Malloc.cpp
which illustrates more or less how to do this.
in config.h.
#define ACE_NTRACE 0
For examples of how this works, please check out