diff options
author | sumedh <sumedh@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1997-02-17 00:09:24 +0000 |
---|---|---|
committer | sumedh <sumedh@ae88bc3d-4319-0410-8dbf-d08b4c9d3795> | 1997-02-17 00:09:24 +0000 |
commit | c685695d30d92cc376ab180641e5faf73cd2b8c1 (patch) | |
tree | a98ed952c2f465e5bdf9df6d02da29ba7899ad59 /TAO/IIOP | |
parent | c80c06ac96191632bddc35badae5886a7171ec4e (diff) | |
download | ATCD-c685695d30d92cc376ab180641e5faf73cd2b8c1.tar.gz |
Added IIOP files
Diffstat (limited to 'TAO/IIOP')
65 files changed, 20270 insertions, 0 deletions
diff --git a/TAO/IIOP/lib/bridge/Makefile b/TAO/IIOP/lib/bridge/Makefile new file mode 100644 index 00000000000..c1f7a3ac3ba --- /dev/null +++ b/TAO/IIOP/lib/bridge/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 1.4 95/09/25 + +FILES = \ + iioporb.cpp iiopobj.cpp connmgr.cpp giop.cpp \ + invoke.cpp svrrqst.cpp tcpoa.cpp + +ROOT = ../.. + +include ../Makefile.gen diff --git a/TAO/IIOP/lib/bridge/connmgr.cpp b/TAO/IIOP/lib/bridge/connmgr.cpp new file mode 100644 index 00000000000..3cdde3c1d76 --- /dev/null +++ b/TAO/IIOP/lib/bridge/connmgr.cpp @@ -0,0 +1,807 @@ +// @(#)connmgr.cpp 1.4 95/09/29 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP: Simple asymmetric TCP connection manager +// +// This has been multithreaded with a very simple strategy, optimizing +// for "lightly threaded" clients rather than maximal sharing of system +// resources (connections, time) in concurrent environments. Two locks +// are used, one each for client and server sides. +// +// The expectation is: threads have a refcount on an endpoint only while +// a call's active. Between calls, they release the endpoint record. If +// need be, the file descriptor in the record may be set to a negative +// number, and the descriptor closed (e.g. on unrecoverable error). +// +// The tricky issues have been strongly avoided. Particularly, on any +// given connection no multiplexing is done; that simplifies this code +// substantially, as well as the protocol code that'd otherwise need to +// dispatch IIOP replies to arbitrary client threads. This costs most if +// several "long" (time-wise) calls are made concurrently. +// +// Similarly, condition variables aren't used to allow concurrent access +// to connection tables during "long" operations: name service lookups, +// connection establishment, or both. Initial connection establishment, +// including use of hostname aliases, pays this cost. +// + +#include <assert.h> +#include <memory.h> +#include <string.h> + +#if unix +# include <netdb.h> +# include <unistd.h> + +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/time.h> + +# include <netinet/in.h> + +#else // unix +# include <winsock.h> + +#endif // unix + +#include <corba/orb.hh> +#include <corba/stub.hh> + +#include "bridge/connmgr.hh" +#include "runtime/thread.hh" +#include "runtime/debug.hh" + + +// +// It's mostly older platforms, but sometimes it's hard to get declarations +// for socket calls, variables, utilities ... C++ requires them. +// +#if !defined (DECLARED_H_ERRNO) +extern int h_errno; +#endif + +#if !defined (DECLARED_ACCEPT) +extern "C" int accept(int, void *, int *); +#endif + +#if !defined (DECLARED_BIND) +extern "C" int bind(int, const void *, int); +#endif + +#if !defined (DECLARED_CONNECT) +extern "C" int connect(int, const void *, int); +#endif + +#if !defined (DECLARED_LISTEN) +extern "C" int listen(int, int); +#endif + +#if !defined (DECLARED_SELECT) && !defined (SELECT_INT_STAR) +extern "C" int select (int, fd_set *, fd_set *, fd_set *, struct timeval *); +#endif + +#if !defined (DECLARED_SETSOCKOPT) +extern "C" int setsockopt (int, int, int, const char *, int); +#endif + +#if !defined (DECLARED_SOCKET) +extern "C" int socket (int, int, int); +#endif + +#if !defined (DECLARED_BZERO) && !defined (bzero) + // in case FD_ZERO needs it +#define bzero(addr, cnt) memset(addr, 0, cnt) +#endif + + +#if defined (SELECT_INT_STAR) +# define SELECT_ARG_CAST (int *) +#else +# define SELECT_ARG_CAST +#endif + + +// +// We tell the kernel to queue no more than LISTEN_LIMIT connection +// requests ... traditionally, BSD implementations max out at 5, but +// more recent implementations have no OS limit. +// +#define LISTEN_LIMIT 5 // traditional maximum + +// +// Lists holding the connections managed in this module: one for outgoing +// connections (client list), the other for incoming ones (server list). +// +// NOTE: with multiple OAs, it'll be desirable to let each OA have its own +// server endpoint list so OAs can manage requests (and their threading) +// separately. +// +static client_endpoint *client_list; +static server_endpoint *server_list; + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up locks covering access to +// both client and server side connection lists. They're separate +// to avoid deadlocking, e.g. self-deadlock when a process calls to +// an object it implements. +// +static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t server_lock = PTHREAD_MUTEX_INITIALIZER; + +// +// We need to cleanly indicate to select()ing server threads that a +// a connection has been returned to their purview. The simplest way +// is for them to wake up normally ... e.g. by data arriving. We use +// writes on a private pipe: that's what signal_fd is for. +// +static int signal_fd; + +// +// Conceptually, each TCP OA listens to a single "in" signal FD. But +// we only support one of them for now. +// +static int signal_in_fd; + +#endif // _POSIX_THREADS + + +// +// Release ... must be reentrant in threaded systems. +// +void +client_endpoint::release () +{ +#ifdef _POSIX_THREADS + Critical region (&client_lock); +#endif // _POSIX_THREADS + + assert (refcount == 1); + refcount--; +} + + +// +// Gets or makes a connection to the indicated host@port, or reports an +// exception explaining why it couldn't. +// +client_endpoint * +client_endpoint::lookup ( + char *host, + unsigned short port, + CORBA_Environment &env +) +{ + client_endpoint *list; + hostent *hp = 0; + +#ifdef _POSIX_THREADS + Critical region (&client_lock); +#endif // _POSIX_THREADS + + // + // see if it's already in the list. if we find it here, we've + // saved a costly/remote name service lookup. + // + // THREADING NOTE: a policy decision is made here to use a different + // connection when an existing one is in use. As with all policies, + // there are cases where different decisions would be in some sense + // more optimal. The rationale is primarily that simpler MT code is + // preferable; blocking until the connection is idle again can easily + // deadlock mutually recursive invocations, and performance tradeoffs + // don't argue universally for multiplexing connections. + // + for (list = client_list; list != 0; list = list->next) { + if (list->port == port) { + if (list->fd == -1) { + dmsg ("client, dead FD in endpoint table"); + continue; + } + if (strcmp (list->hostname, host) == 0) { + if (list->refcount == 0) { + list->refcount++; + return list; + } else { + // + // find/make a different connection, this one + // is busy for the moment + // + continue; + } + } + + // else maybe one's an address, one's a name + // or one's a FQDN, one's not fully qualified + // ... + } + } + + // + // See if we can find the host's address. This handles two styles + // of hostname: domain names (including partially qualified names, + // which rely on some implicit location in the DNS hierarchy), and + // "dotted-decimal" notation (e.g. "192.9.200.1"). Both forms are + // required by Internet standards (and hence IIOP). + // + // THREADING NOTE: gethostbyname is a "long" call, it'd often be worth + // dropping the lock during this call. It'd complicate control flow + // though, so until heavily threaded clients are common it's left + // to work in this simple way. + // + // XXX note that some platforms, particularly older ones no longer + // being actively maintained, violate Internet standards and don't + // accept dotted-decimal hostnames. + // + if (hp == 0) { + while ((hp = gethostbyname (host)) == 0) { + switch (h_errno) { + case TRY_AGAIN: // soft error + // sleep (1); + continue; + + case HOST_NOT_FOUND: // hard NAK (not-exist) + dmsg1 ("gethostbyname '%s' --> No such host", host); + env.exception (new CORBA_OBJECT_NOT_EXIST (COMPLETED_NO)); + return 0; + + case NO_RECOVERY: // hard error + case NO_DATA: // maybe found an MX record? + default: // nonstandard error code + dmsg2 ("gethostbyname '%s' --> h_errno %d", host, h_errno); + env.exception (new CORBA_COMM_FAILURE (COMPLETED_NO)); + return 0; + } + } + // + // Here we found the address associated with the hostname. + // + // NOTE: if we save addresses in the connection table, we might + // rescan it on the grounds that maybe we got a hostname alias + // (e.g. not the DNS CNAME). No functionality lost if we don't, + // but in some cases we'd save a connection. + // + } + + // + // Here we've decided to grow the set of connections to satisfy + // this request. We get the record and then fill it out. + // + // NOTE: Should first shrink the list if it's very large! We could + // track time of last use to support LRU purging of connection cache, + // with potential removing of duplicates. + // + list = new client_endpoint; + + if ((list->fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { + dsockerr ("client socket"); + delete list; + + env.exception (new CORBA_UNKNOWN (COMPLETED_NO)); + return 0; + } + + // + // SECURITY NOTE: Some networks routinely configure bridges based on + // source and destination port. So it may be important to bind this + // socket to some preestablished port before connecting, since without + // doing so the traffic may not be passed through a firewall or bridge. + // + + + // + // Connect to the desired server address. + // + // THREADING NOTE: this is again a "long" call, during which it'll be + // worth dropping the lock on the connection list some day when many + // client threads contend on that lock. + // + sockaddr_in addr; + + memset (&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = *(long *)hp->h_addr; + addr.sin_port = htons (port); + + if (connect (list->fd, (sockaddr *) &addr, sizeof addr) < 0) { + dsockerr ("client connect"); + dmsg2 ("... connect failure was to '%s:%d'", host, port); + delete list; + + env.exception (new CORBA_COMM_FAILURE (COMPLETED_NO)); + return 0; + } + + list->hostname = strdup (host); + list->port = port; + list->refcount = 1; + list->next = client_list; + + client_list = list; + + return list; +} + + +#ifdef DEBUG + +void +client_endpoint::dump (FILE *file) +{ + client_endpoint *list; + +#ifdef _POSIX_THREADS + // + // NOTE that although this lock is held for a _very_ long time + // (terminal/stderr I/O is much slower than network I/O and this + // does "lots" of it) we don't have much of an option because we + // need to present a single, consistent view of this table. + // + Critical region (&client_lock); +#endif // _POSIX_THREADS + + fprintf (file, "List of client-side connections:\n"); + + for (list = client_list; list != 0; list = list->next) { + fprintf (file, " %s @ %d\tfd %d\trefcnt %d\n", + list->hostname, list->port, list->fd, + list->refcount); + } + fprintf (file, "\n"); +} + +#endif + + + +// +// Release ... must be reentrant in threaded systems. +// +// NOTE: this version actually does two things, which could be split +// into two separate routines with some TBD effect on performance. It +// decrements the use count of this connection; and it informs other +// potential reading threads that it's OK to read incoming messages. +// +// Splitting these two apart could let the server issue Reply messages +// in arbitrary orders, at the potential cost of putting extra context +// switching into the critical path for request handling. +// +void +server_endpoint::release () +{ +#ifdef _POSIX_THREADS + Critical region (&server_lock); +#endif // _POSIX_THREADS + + assert (refcount == 1); + refcount--; + +#ifdef _POSIX_THREADS + // + // Tell whoever's in block_for_input() that they can look again + // at this connection, reading messages off of it and replying + // to them as appropriate. + // + (void) write (signal_fd, "b", 1); +#endif // _POSIX_THREADS +} + + +// +// Initialize a server endpoint, at the specified port or, if that port +// number is zero, then at any available port. +// +// XXX at some point this will include an interface name, to facilitate +// its use on multihomed hosts such as firewalls. +// +server_endpoint * +server_endpoint::initialize ( + unsigned short &port, + // XXX char *ifname + CORBA_Environment &env +) +{ +#ifdef _POSIX_THREADS + Critical region (&server_lock); +#endif // _POSIX_THREADS + + // + // XXX at this time, we only support one port/listener per process. + // This restriction should be lifted sometime. + // + if (server_list != 0) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + + // + // Initial "connection" record. + // + server_endpoint *list; + + list = new server_endpoint; + list->is_passive = CORBA_B_TRUE; + list->port = port; + list->next = 0; + list->refcount = 0; + + // + // Create the socket + // + if ((list->fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { + dsockerr ("server socket"); + delete list; + + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + + // + // Bind it to the requested port, if one was requested. + // + sockaddr_in addr; + + if (port != 0) { +#ifdef SO_REUSEADDR + // + // In cases where servers abort and must be restarted, we + // want to avoid TCP's mandatory 4 minute close-wait timeout. + // So we set SO_REUSEADDR only on the "listening" socket, + // which never actually gets a connection; it's safe to be + // "reusing" the address since the OS never reuses TCP ports + // which are in the BOUND or LISTEN states. + // + // If we can't do this, it's not an error -- this is just an + // optimization applicable to some failure cases, we can live + // without it in all cases. Applications might care; if so, + // they should run on platforms supporting SO_REUSEADDR. + // + int flag = 1; + + if (setsockopt (list->fd, SOL_SOCKET, SO_REUSEADDR, + (char *) &flag, sizeof (flag)) < 0) { + dsockerr ("server setsockopt SO_REUSEADDR"); + } +#endif // SO_REUSEADDR + + memset (&addr, 0, sizeof addr); + addr.sin_family = AF_INET; + addr.sin_port = htons (port); + + // + // XXX someday, this is where we'll bind to specific interfaces + // on multihomed hosts (e.g. firewalls) which do no routing. + // + addr.sin_addr.s_addr = htonl (INADDR_ANY); + + if (bind (list->fd, (sockaddr *)&addr, sizeof addr) < 0) { + dsockerr ("server bind"); + closesocket (list->fd); + delete list; + + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + } + + // + // Make it a listening (passive) socket + // + if (listen (list->fd, LISTEN_LIMIT) < 0) { + dsockerr ("server listen"); + closesocket (list->fd); + delete list; + + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + + // + // If we bound to a system-assigned port, find out which port + // address the system assigned us. + // + if (port == 0) { + int size = sizeof (addr); + + if (getsockname (list->fd, (sockaddr *) &addr, &size) < 0) { + dsockerr ("server getsockname"); + closesocket (list->fd); + delete list; + + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + port = list->port = ntohs (addr.sin_port); + } + +#ifdef _POSIX_THREADS + // + // We need a clean way to have other threads signal ones that + // are select()ing that there's another connection they need to + // pay attention to. So we set up a pipe for them to use. + // + int pipefd [2]; + + if (pipe (pipefd) != 0) { + dperror ("pipe for connection manager"); + closesocket (list->fd); + delete list; + + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + signal_in_fd = pipefd [0]; + signal_fd = pipefd [1]; +#endif // _POSIX_THREADS + + server_list = list; + + return list; +} + + +// +// Get a connection. Unless "eager" is set, the connection returned +// will actually have data ready for input. Normally, unthreaded +// environments can't be "eager", and threaded environments prefer to +// use that model to achieve better performance. Threaded environments +// can of course not be "eager". +// +// THREADING NOTE: It's undesirable to have more than one thread call this +// at the same time; the semantics of two threads that select() on the same +// file descriptor are undefined. Hence the static flag that's tested. +// +server_endpoint * +server_endpoint::block_for_connection ( + CORBA_Boolean eager, + timeval *timeout, + CORBA_Environment &env +) +{ +#ifdef _POSIX_THREADS + Critical region (&server_lock); +#endif // _POSIX_THREADS + + // + // Head of the list is a passive file descriptor. The rest is a list + // of ones used for I/O to clients. Only call block_for_input() on + // endpoints returned by initialize(). + // + assert (is_passive); + + // + // Scan the list of server-side connections and generate the fd_set + // we'd use in a select() call (or eagerly return a file descriptor, + // without selecting first). Make the call, examine the results; + // maybe we return soon to the caller, maybe we don't. + // + // XXX if there are lots of connections here we should contemplate + // shutting down several of them in order to gracefully reclaim the + // OS resources associated with the connections. + // + for (;;) { + fd_set read_fdset; + server_endpoint *list, *previous; + int max_fd = 0; + + FD_ZERO (&read_fdset); + for (list = this, previous = 0; + list; + previous = list, list = list->next) { + + // + // Delete records for connections that were closed and + // which nobody is using. + // + if (list->fd < 0) { + if (list->refcount != 0) + continue; + + assert (previous != 0); // passive must exist! + + previous->next = list->next; + delete list; + list = previous; + continue; + } + + // + // If nobody else is reading from this connection, we work with + // it ... if the caller is "eager" we return it immediately + // (even with no data). Else we prepare to select on it. + // + // Refcount is currently used to track if someone's reading, + // though it'd be easy to further distinguish "someone reading" + // from "someone needs". A "needed" connection without someone + // currently reading could be assigned a thread to read it; + // that would enable out-of-order processing (a lock would be + // needed to ensure no interleaving of GIOP 1.1 fragments). + // + if (list->refcount == 0) { + if (eager && !list->is_passive) { + list->refcount++; + return list; + } + FD_SET (list->fd, &read_fdset); + if (list->fd > max_fd) + max_fd = list->fd; + } + } + + // + // Select until something interesting happens. + // + // THREADING NOTE: We leave the critical section for the duration + // of this select() since we'll normally be there a long time, and + // other threads must grab server_lock while we block. But we must + // reenter it later to compare the select() output with the set of + // legal server side connections. + // + // Also, since the semantics of multiple threads calling select() + // on the same file descriptor(s) is undefined, we prevent that. + // + // We add the pipe file descriptor to the list so that when other + // threads release connections, we can learn about this. + // +#ifdef _POSIX_THREADS + static int doing_select; // = 0 + + if (doing_select) { + dmsg ("concurrent block_for_input() calls"); + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + return 0; + } else + doing_select = 1; + + region.leave (); + + FD_SET (signal_in_fd, &read_fdset); + if (signal_in_fd > max_fd) + max_fd = signal_in_fd; +#endif // _POSIX_THREADS + + int value = select (max_fd + 1, SELECT_ARG_CAST &read_fdset, + 0, 0, timeout); + +#ifdef _POSIX_THREADS + region.enter (); + doing_select = 0; +#endif // _POSIX_THREADS + + if (value < 0) { + dsockerr ("server select"); + env.exception (new CORBA_COMM_FAILURE (COMPLETED_NO)); + return 0; + } else if (value == 0) { + dmsg ("server select timed out"); + return 0; + } + + // + // Check out the set of FDs we found out about in select() above. + // If accept() is needed, do so and rescan. Else return an entry + // from the list. + // + // THREADING NOTE: we read any byte written by another thread + // to wake us up. Rare to have more than one such byte! + // +#ifdef _POSIX_THREADS + if (FD_ISSET (signal_in_fd, &read_fdset)) { + char b; + (void) read (signal_in_fd, &b, 1); + if (debug_level >= 5) + dmsg ("block_for_input() woken up"); + } +#endif // _POSIX_THREADS + + for (list = this; list; list = list->next) { + if (list->fd == -1 || !FD_ISSET (list->fd, &read_fdset)) + continue; + + // + // If we've got one with incoming data, return it. + // + if (!list->is_passive) { + list->refcount++; + return list; + } + + // + // Nyet ... incoming connection. Accept it. + // + sockaddr_in saddr; + int saddr_siz = sizeof saddr; + int new_fd; + + if ((new_fd = accept (list->fd, + (sockaddr *) &saddr, &saddr_siz)) < 0) { + dsockerr ("server accept"); + continue; // what else? + } + + server_endpoint *new_clnt; + + new_clnt = new server_endpoint; + new_clnt->fd = new_fd; + new_clnt->port = saddr.sin_port; + new_clnt->is_passive = CORBA_B_FALSE; + new_clnt->refcount = 0; + + dmsg1 ("accepted new FD %d", fd); + + // + // Splice it into list betwen here and next. Since most + // systems can't piggyback data with the connection setup + // packet, there's probably no data here yet. We can't + // find out when it arrives without blocking or polling. + // + new_clnt->next = list->next; + list->next = new_clnt; + list = new_clnt; + + // + // One ramification of an "eager" model: we treat the + // connection as having data immediately on connection + // establishment. Lacking transactional TCP this will not + // often be the case ... but the basic "eager" model is + // that we spend LWPs (threads) to improve latencies. + // + if (eager) { + new_clnt->refcount++; + return new_clnt; + } + } + } +} + +void +server_endpoint::shutdown_connections ( + void (*close_conn) (int &, void *), + void *info +) +{ + server_endpoint *list, *successor; + +#ifdef _POSIX_THREADS + Critical region (&server_lock); +#endif // _POSIX_THREADS + + for (list = this; list != 0; list = successor) { + if (list->is_passive) + (void) closesocket (list->fd); + else + close_conn (list->fd, info); + + successor = list->next; + delete list; + } +} + + +#ifdef DEBUG + +void +server_endpoint::dump (FILE *file) +{ + server_endpoint *list; + +#ifdef _POSIX_THREADS + // + // NOTE the comment in client_endpoint::dump() re long lock times. + // + Critical region (&client_lock); +#endif // _POSIX_THREADS + + fprintf (file, "List of server-side connections:\n"); + + for (list = server_list; list != 0; list = list->next) { + fprintf (file, " port %d%s\tfd %d\trefcnt %d\n", + list->port, list->is_passive ? " (passive)" : "", + list->fd, list->refcount); + } + fprintf (file, "\n"); +} + +#endif + diff --git a/TAO/IIOP/lib/bridge/connmgr.hh b/TAO/IIOP/lib/bridge/connmgr.hh new file mode 100644 index 00000000000..2a2a55fb6ab --- /dev/null +++ b/TAO/IIOP/lib/bridge/connmgr.hh @@ -0,0 +1,141 @@ +// @(#)connmgr.hh 1.2 95/09/24 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Simple threaded asymmetric TCP connection manager. +// + +#ifndef _CONNMGR_HH +#define _CONNMGR_HH + +#if unix +# include <sys/time.h> +#else // __WIN32__ +# include <winsock.h> +#endif + +#ifdef DEBUG +#include <stdio.h> +#endif + +// +// Utility class that may not really belong here; it's currently used only +// with connection endpoints though. +// +// THREADING NOTE: note that the pointer doesn't change, so two threads +// could share an "autorelease<T>" value if they collaborated about safe +// refcounting and destruction. +// +template <class T> +class autorelease { + public: + autorelease &operator = (T *ptr) + { + if (_state) _state->release(); + _state = ptr; return *this; + } + operator int () { return _state ? 1 : 0; } + T *operator -> () { return _state; } + autorelease () { _state = 0; } + ~autorelease () + { if (_state) _state->release (); } + + private: + T *_state; +}; + + +// +// Client endpoints are acquired just by looking them up; they're created +// if needed, and may be closed when they're not in use. +// +// NOTE: timeout on lookup/create would be a nice quality-of-service +// knob to be able to tweak... +// +struct client_endpoint { + int fd; + + static client_endpoint *lookup ( + char *host, + unsigned short port, + CORBA_Environment &env + ); + + void release (); + +#ifdef DEBUG + static void dump (FILE *); +#endif + + private: + char *hostname; + unsigned short port; + unsigned refcount; + client_endpoint *next; + +#ifdef __GNUG__ + // + // G++ (even 2.6.3) stupidly thinks instances can't be + // created This de-warns. + // + friend class everyone_needs_a_friend; +#endif +}; + + +// +// Server endpoints start out by waiting passively for input on a port. +// New connections may be created. Old ones may be disconnected. +// +// NOTE: this version doesn't bind to specific host addresses on +// multihomed hosts, so it's not yet suitable for use on firewalls. +// +// NOTE: this version also doesn't listen on more than one port at a time. +// +class server_endpoint { + public: + int fd; + + static server_endpoint *initialize ( + unsigned short &port, + // XXX char *ifname, + CORBA_Environment &env + ); + + server_endpoint *block_for_connection ( + CORBA_Boolean eager, + timeval *timeout, + CORBA_Environment &env + ); + + void shutdown_connections ( + // add a flag saying some, all? + void (*close_conn)( + int &fd, + void *info + ), + void *info + ); + + void release (); + +#ifdef DEBUG + static void dump (FILE *); +#endif + + private: + unsigned short port; + CORBA_Boolean is_passive; + unsigned refcount; + server_endpoint *next; + +#if defined (__GNUG__) + // + // G++ (even 2.6.3) stupidly thinks instances can't be + // created. This de-warns. + // + friend class everyone_needs_a_friend; +#endif +}; + +#endif // _CONNMGR_HH diff --git a/TAO/IIOP/lib/bridge/giop.cpp b/TAO/IIOP/lib/bridge/giop.cpp new file mode 100644 index 00000000000..a95aede47fb --- /dev/null +++ b/TAO/IIOP/lib/bridge/giop.cpp @@ -0,0 +1,1355 @@ +// @(#)giop.cpp 1.10 95/09/21 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// GIOP: Utility routines for sending, receiving GIOP messages +// +// Note that the Internet IOP is just the TCP-specific mapping of +// the General IOP. Areas where other protocols may map differently +// include use of record streams (TCP has none), orderly disconnect +// (TCP has it), endpoint addressing (TCP uses host + port), security +// (Internet security should be leveraged by IIOP) and more. +// +// NOTE: There are a few places where this code knows that it's really +// talking IIOP instead of GIOP. No rush to fix this so long as we are +// really not running atop multiple connection protocols. +// +// THREADING NOTE: currently, the connection manager eliminates tricky +// threading issues by providing this code with the same programming model +// both in threaded and unthreaded environments. Since the GIOP APIs were +// all designed to be reentrant, this makes threading rather simple! +// +// That threading model is that the thread making (or handling) a call is +// given exclusive access to a connection for the duration of a call, so +// that no multiplexing or demultiplexing is needed. That is, locking is +// at the "connection level" rather than "message level". +// +// The down side of this simple threading model is that utilization of +// system resources (mostly connections, but to some extent network I/O) +// in some kinds of environments can be inefficient. However, simpler +// threading models are much easier to get properly debugged, and often +// perform better. Also, such environments haven't been seen to be any +// kind of problem; the model can be changed later if needed, it's just an +// internal implementation detail. Any portable ORB client is not allowed +// to rely on semantic implications of such a model. +// +// XXX there is lots of unverified I/O here. In all cases, if an error +// is detected when marshaling or unmarshaling, it should be reported. +// + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if unix +# include <sys/types.h> +# include <sys/socket.h> + + // Use normal file I/O where possible, it's better tuned. +# define send(s,buf,len,zero) write(s,buf,len) +# define recv(s,buf,len,zero) read(s,buf,len) + +#else +# include <winsock.h> + +#endif + +#include <corba/orb.hh> + +#include "runtime/cdr.hh" +#include "runtime/debug.hh" +#include "runtime/thread.hh" +#include "bridge/giop.hh" + + +#if !defined (DECLARED_SHUTDOWN) +extern "C" int shutdown (int fd, int how); +#endif + + +#define GIOP_HDR_LEN 12 // defined by GIOP 1.0 protocol + + +#ifdef _POSIX_THREADS +// +// This lock covers the mutable info in all IIOP objref data, +// namely the forwarded-to objref. It must be held when a client +// thread is reading or modifying that data, to prevent one from +// overwriting data the other's reading or writing. +// +static pthread_mutex_t fwd_info_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + +// +// Apart from the length word, headers are specified to be arrays of +// bytes. They're dealt with as such, rather than using CDR routines, +// to speed up the critical paths for message read and write. +// +static inline CORBA_Boolean +start_message ( + GIOP::MsgType type, + CDR &msg +) +{ + msg.next = msg.buffer; // for reused streams + msg.remaining = msg.length; + + if (msg.bytes_remaining () < GIOP_HDR_LEN) + return CORBA_B_FALSE; + + msg.next [0] = 'G'; + msg.next [1] = 'I'; + msg.next [2] = 'O'; + msg.next [3] = 'P'; + + msg.next [4] = GIOP::MY_MAJOR; + msg.next [5] = GIOP::MY_MINOR; + msg.next [6] = MY_BYTE_SEX; + msg.next [7] = (unsigned char) type; + + msg.skip_bytes (GIOP_HDR_LEN); + return CORBA_B_TRUE; +} + +#ifdef DEBUG +static const char digits [] = "0123456789ABCD"; +static const char *names [] = { + "Request", "Reply", "CancelRequest", + "LocateRequest", "LocateReply", + "CloseConnection", "MessageError" +}; + +static void +dump_msg (const char *label, const unsigned char *ptr, size_t len) +{ + if (debug_level >= 2) { + dmsg_v ("%s GIOP v%c.%c msg, %d data bytes, %s endian, %s\n", + label, digits [ptr [4]], digits [ptr [5]], + len - GIOP_HDR_LEN, + (ptr [6] == MY_BYTE_SEX) ? "my" : "other", + (ptr [7] <= GIOP::MessageError) + ? names [ptr [7]] : "UNKNOWN TYPE"); + + if (debug_level >= 4) + dmsg_opaque_full("data bytes", ptr, len); + } +} + +#else // !DEBUG +#define dump_msg(label,ptr,len) +#endif // !DEBUG + +CORBA_Boolean +GIOP::send_message ( + CDR &stream, + int &connection +) +{ + char *buf = (char *) stream.buffer; + size_t buflen = stream.next - stream.buffer; + int writelen; + + assert (buflen == (stream.length - stream.remaining)); + + // + // Patch the message length in the GIOP header; it's always at the + // same eight byte offset into the message. + // + // NOTE: Here would also be a fine place to calculate a digital + // signature for the message and place it into a preallocated + // slot in the "ServiceContext". Similarly, this is a good spot + // to encrypt messages (or just the message bodies) if that's + // needed in this particular environment and that isn't handled + // by the networking infrastructure (e.g. IPSEC). + // + *(CORBA_Long *)(stream.buffer + 8) = + (CORBA_Long) (buflen - GIOP_HDR_LEN); + + // + // Strictly speaking, should not need to loop here because the + // socket never gets set to a nonblocking mode ... some Linux + // versions seem to need it though. Leaving it costs little. + // + dump_msg ("send", stream.buffer, buflen); + while (buflen > 0) { + if (buflen > stream.length) { + dmsg2 ("?? writebuf, buflen %u > length %u\n", + buflen, stream.length); + return CORBA_B_FALSE; + } + writelen = send (connection, (char _FAR *) buf, buflen, 0); + +#ifdef DEBUG + dmsg_filter (6, "wrote %d bytes to connection %d", + writelen, connection); +#endif // DEBUG + + assert ((writelen >= 0 && + ((size_t)writelen) <= buflen) || writelen == -1); + + // + // On error or EOF, report the fault, close the connection, and + // mark it as unusable/defunct. + // + // XXX on client side write errors, we may hit the case that + // the server did a clean shutdown but we've not yet read the + // GIOP::CloseConnection message. If we get an error, we need + // to see if there is such a message waiting for us, and if so + // we should cause (full) rebinding to take place. + // + if (writelen == -1) { + dsockerr ("OutgoingMessage::writebuf()"); + dmsg1 ("closing conn %d after fault", connection); + closesocket (connection); + connection = -1; + return CORBA_B_FALSE; + } else if (writelen == 0) { + dmsg1 ("OutgoingMessage::writebuf() ... EOF, closing conn %d", + connection); + closesocket (connection); + connection = -1; + return CORBA_B_FALSE; + } + if ((buflen -= writelen) != 0) + buf += writelen; + +#ifdef DEBUG + // + // NOTE: this should never be seen. However, on Linux + // it's been seen with UNIX domain sockets. + // + if (buflen) + dmsg_filter (8, "%u more bytes to write...\n", buflen); +#endif + } + return CORBA_B_TRUE; +} + + +// +// Server sends an "I'm shutting down now, any requests you've sent me +// can be retried" message to the server. The message is prefab, for +// simplicity. +// +// NOTE: this is IIOP-specific though it doesn't look like it is. It +// relies on a TCP-ism: orderly disconnect, which doesn't exist in all +// transport protocols. Versions of GIOP atop some transport that's +// lacking orderly disconnect must define some transport-specific +// handshaking (e.g. the XNS/SPP handshake convention) in order to know +// that the same transport semantics are provided when shutdown is begun +// with messages "in flight". (IIOP doesn't report false errors in the +// case of "clean shutdown", because it relies on orderly disconnect as +// provided by TCP. This quality of service is required to write robust +// distributed systems.) +// +static const char +close_message [GIOP_HDR_LEN] = { + 'G', 'I', 'O', 'P', + GIOP::MY_MAJOR, GIOP::MY_MINOR, MY_BYTE_SEX, GIOP::CloseConnection, + 0, 0, 0, 0 +}; + + +void +GIOP::close_connection ( + int &fd, + void * // currently unused +) +{ + // + // It's important that we use a reliable shutdown after we send + // this message, so we know it's received. + // + // XXX should recv and discard queued data for portability; note + // that this won't block (long) since we never set SO_LINGER + // + dump_msg ("send", (const unsigned char *) close_message, GIOP_HDR_LEN); + (void) send (fd, close_message, GIOP_HDR_LEN, 0); + (void) shutdown (fd, 2); + (void) closesocket (fd); + dmsg1 ("shut down socket %d", fd); + fd = -1; +} + +// +// Send an "I can't understand you" message -- again, the message is +// prefabricated for simplicity. This implies abortive disconnect +// (at the application level, if not at the level of TCP). +// +// NOTE that IIOP will still benefit from TCP's orderly disconnect. +// +static const char +error_message [GIOP_HDR_LEN] = { + 'G', 'I', 'O', 'P', + GIOP::MY_MAJOR, GIOP::MY_MINOR, MY_BYTE_SEX, GIOP::MessageError, + 0, 0, 0, 0 +}; + +static inline void +send_error (int &fd) +{ + dump_msg ("send", (const unsigned char *) error_message, GIOP_HDR_LEN); + (void) send (fd, error_message, GIOP_HDR_LEN, 0); + (void) shutdown (fd, 2); + (void) closesocket (fd); + dmsg1 ("aborted socket %d", fd); + fd = -1; +} + + +// +// Loop on data read ... this is required with some implementations of +// sockets (e.g. winsock, HP/UX) since even when async mode is not set, +// recv() won't block until the requested amount of data is available. +// +static int +read_buffer ( + int fd, + char *buf, + size_t len +) +{ + int bytes_read = 0; + + while (len != 0) { + int retval; + + retval = recv (fd, buf, len, 0); + +#ifdef DEBUG + dmsg_filter (6, "read %d bytes from connection: %d", retval, fd); +#endif + if (retval <= 0) // EOF or error + return retval; + + len -= retval; + buf += retval; + bytes_read += retval; + } + + return bytes_read; +} + + +// +// Read the message header, plus any data part of the message, setting +// stuff up so that CDR byteswaps data as appropriate. Errors are reported +// to be MessageError messages. +// +// NOTE: this code is structured to issue two read() calls for each +// incoming message. Alternative structures (e.g. with a user-space buffer +// per connection, or networking code handing off entire GIOP messages) can +// reduce the overhead of these calls to the networking code; correctness +// and simplicity drove this implementation more than efficiency. +// +// NOTE: as always, counting system calls associated with I/O gives you +// a good basic understanding of the tuning issues. On the server side, +// there is normally select/read/read/write per invocation. The call to +// select() can be omitted by allocating a thread to each connection; in +// some cases, that alone has almost doubled performance. The two read() +// calls can be made into one by fancy buffering. How fast could it be +// with both optimizations applied? +// +GIOP::MsgType +GIOP::read_message ( + int &connection, + CDR &msg, + CORBA_Environment &env +) +{ + GIOP::MsgType retval; + CORBA_ULong message_size; + + // + // Read the message header off the wire. + // + // THREADING NOTE: the connection manager handed us this connection + // for exclusive use, so we need not worry about having two threads + // interleave reads of partial messages. This model is excellent + // for "lightly threaded" systems (as will be the majority in the + // near future) but makes less effective use of connection resources + // as the "duty factor" goes down because of either long calls or + // bursty contention during numerous short calls to the same server. + // + assert (msg.length > GIOP_HDR_LEN); + + msg.next = msg.buffer; + msg.remaining = GIOP_HDR_LEN; + + char *bufptr = (char _FAR *) msg.buffer; + int len; + + // + // Read the header into the buffer. + // + if ((len = read_buffer (connection, bufptr, GIOP_HDR_LEN)) + != GIOP_HDR_LEN) { + if (len == 0) { // EOF + dmsg1 ("Header EOF ... peer probably aborted connection %d", + connection); + + // + // XXX should probably find some way to report this without + // an exception, since for most servers it's not an error. + // Is it _never_ an error? Not sure ... + // + } else if (len < 0) { // error + dsockerr ("GIOP::read_message header"); + } else { // short read ... + dmsg ("read message header failed (short)"); + } + env.exception (new CORBA_COMM_FAILURE (COMPLETED_MAYBE)); + return MessageError; + } + + // + // NOTE: if message headers, or whome messages, get encrypted in + // application software (rather than by the network infrastructure) + // they should be decrypted here ... + // + + // + // First make sure it's a GIOP message of any version. + // + if (!(msg.buffer [0] == 'G' && msg.buffer [1] == 'I' + && msg.buffer [2] == 'O' && msg.buffer [3] == 'P')) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); // header + dmsg ("bad header, magic word"); + return MessageError; + } + + // + // Then make sure the major version is ours, and the minor version is + // one that we understand. + // + if (!(msg.buffer [4] == MY_MAJOR && msg.buffer [5] <= MY_MINOR)) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); // header + dmsg ("bad header, version"); + return MessageError; + } + + // + // Get the message type out and adjust the buffer's records to record + // that we've read everything except the length. + // + retval = (GIOP::MsgType) msg.buffer [7]; + msg.skip_bytes (8); + + // + // Make sure byteswapping is done if needed, and then read the message + // size (appropriately byteswapped). + // + msg.do_byteswap = (msg.buffer [6] != MY_BYTE_SEX); + msg.get_ulong (message_size); + + // + // Make sure we have the full length in memory, growing the + // buffer if needed. + // + // NOTE: We could overwrite these few bytes of header... they're + // left around for now as a debugging aid. + // + assert (message_size <= UINT_MAX); + + if ((GIOP_HDR_LEN + message_size) > msg.length) + msg.grow ((size_t) (GIOP_HDR_LEN + message_size)); + + msg.remaining = (size_t) message_size; + bufptr = (char *) & msg.buffer [GIOP_HDR_LEN]; + + // + // Read the rest of this message into the buffer. + // + if ((len = read_buffer (connection, bufptr, (size_t) message_size)) + != (int) message_size) { + if (len == 0) { + dmsg1 ("read message body, EOF on fd %d", connection); + } else if (len < 0) { + dperror ("GIOP::read_message() body"); + } else { + dmsg2 ("short read, only %d of %d bytes", len, message_size); + } + + // clean up, and ... + env.exception (new CORBA_COMM_FAILURE (COMPLETED_MAYBE)); // body + dmsg ("couldn't read rest of message"); + return MessageError; + } + + dump_msg ("recv", msg.buffer, (size_t)(message_size + GIOP_HDR_LEN)); + return retval; +} + + +// +// Normal invocations don't involve any heap allocation; messages are +// constructed into stack-based buffers and are read into those buffers +// too. Larger buffers are heap-allocated as needed. +// +// The constraint on request IDs is that no two requests from the same +// client with the same ID are outstanding at the same time. In single +// threaded environments, this is met by any number whatever. When +// multiple threads are used, we eliminate the need for any locked state +// by using the thread ID as the request ID, since any given thread +// has at most one request outstanding at a time. +// +// NOTE: this means that if "deferred synchronous" calls get supported, +// it's done by creating a thread internally to make the call. That is +// less disruptive (and error prone) in general than restructuring an ORB +// core in terms of asynchrony. +// + +GIOP::Invocation::Invocation ( + IIOP_Object *data, + const char *operation, + CORBA_Boolean is_roundtrip +) : + _data (data), + opname (operation), + do_rsvp (is_roundtrip), + stream (&buffer [0], sizeof buffer) +{ +#ifdef _POSIX_THREADS + // + // POSIX does not require this to be true, it's an implementation + // assumption that will at some point be removed but is true on + // many current POSIX.1c implementations. + // + assert (sizeof (CORBA_ULong) == sizeof (pthread_t)); + + my_request_id = (CORBA_ULong) pthread_self (); +#else + my_request_id = 0; +#endif // _POSIX_THREADS +} + +GIOP::Invocation::~Invocation () +{ +} + +// +// Octet codes for the parameters of the "Opaque" (sequence of octet) data +// type used various places internally ... a CDR encapsulation holding two +// parameters (like all sequence TypeCodes). +// +// NOTE: this **MUST** be longword aligned, which is why it's coded as a +// longword array not an octet array. Just sticking a long in for padding +// won't work with compilers that optimize unused data out of existence. +// +static const CORBA_Long _oc_opaque [] = { // CDR typecode octets + 1, // native endian + padding; "tricky" + 10, // ... (sequence of) octets + 0 // ... unbounded +}; +CORBA_TypeCode TC_opaque (tk_sequence, + sizeof _oc_opaque, (unsigned char *) &_oc_opaque, + CORBA_B_FALSE); + + +// +// Octet codes for the parameters of the ServiceContextList TypeCode ... +// this is a CDR encapsulation holding two parameters (like all sequences): +// a TypeCode, and the bounds of the sequence (zero in this case). +// +// This is complicated since the Typecode for the data type for the sequence +// members is complex, a structure that nests two further typecodes (one is +// a sequence). +// +// NOTE: this must be longword aligned! +// +static const CORBA_Long _oc_svc_ctx_list [] = { + // START bytes of encapsulation 0 + 1, // native endian + padding; "tricky" + + // + // FIRST sequence param: typecode for struct is complex, + // and so uses a nested encapsulation. + // + tk_struct, + 72, // length of encapsulation 1 + + // START bytes of encapsulation 1 (struct params) + 1, // native endian + padding; "tricky" + 1, 0, // type ID omitted: null string + 1, 0, // name omitted "ServiceContext" + + 2, // two struct elements + + // + // First structure element: name, typecode for ULong + // + // NOTE: to be more strictly correct this could be a tk_alias + // typecode ... + // + 1, 0, // name omitted: "context_id" + tk_long, + + // + // Second structure element: name, typecode for sequence of octet; + // the typecode for sequence of octet is complex, there's a second + // level of nested encapuslation here. + // + 1, 0, // name omitted: "context_data" + tk_sequence, // sequence typecode + 16, // length of encapsulation 2 + + // START bytes of encapsulation 2 (sequence params) + 1, // native endian + padding; "tricky" + 1, 0, // type ID omitted: null string + tk_octet, // (sequence of) octet + 0, // ... unbounded length + // END bytes of encapsulation 2 (sequence params) + + // END bytes of encapsulation 1 (struct params) + + // + // SECOND sequence param: bound of sequence (none) + // + 0 // unbounded seq of ServiceContext + // END bytes of encapsulation 0 (sequence params) +}; +static CORBA_TypeCode TC_ServiceContextList (tk_sequence, + sizeof _oc_svc_ctx_list, (unsigned char *) &_oc_svc_ctx_list, + CORBA_B_FALSE); + + +// +// The public API involves creating an invocation, starting it, filling +// in request parameters, actually performing the invocation, getting +// response parameters, and then cleaning up. Sometimes they must be +// restarted (e.g. request forwarding). This is the start/restart entry. +// +void +GIOP::Invocation::start ( + CORBA_Environment &env +) +{ + const opaque *key; + + // + // First try to bind to the appropriate address. We do that here since + // we may get forwarded to a different objref in the course of any + // given call, with new start() call each time. It's not cached in + // the objref data since the connections change asynchronously from + // objref invocations and this simplifies connection management. + // + // THREADING NOTE: this connection is reserved to this call. Also, + // starting at this point in the call, new forwarding information will + // not be used until/unless the call is reissued. Correctness is not + // affected, the call will just be forwarded later than it might be + // in a more complex implementation. + // + assert (endpoint == 0); + assert (_data != 0); + +#ifdef _POSIX_THREADS + Critical section (&fwd_info_lock); +#endif // _POSIX_THREADS + + if (_data->fwd_profile != 0) { + key = &_data->fwd_profile->object_key; + endpoint = client_endpoint::lookup ( + (char *)_data->fwd_profile->host, + _data->fwd_profile->port, env); + } else { + key = &_data->profile.object_key; + endpoint = client_endpoint::lookup ( + (char *)_data->profile.host, + _data->profile.port, env); + } + if (env.exception () != 0) { + dexc (env, "invoke, lookup client endpoint"); + return; + } + + // + // POLICY DECISION: If the client expects most agents to forward, then + // it could try to make sure that it's been forwarded at least once by + // eliciting it with a LocateRequest message. (Further hinting in the + // IIOP::ProfileData could help!) + // + // That scenario does not match an "Inter" ORB Protocol well, since + // bridges chain calls rather than forwarding them. It does match + // some kinds of "Intra" ORB scenarios well, with many agents that + // spawn new processes talking to their clients across the net. + // + // At this time, the policy noted above is followed in the sense + // that this software does NOT expect most agents to forward, so it + // doesn't bother to probe. Correctness is not affected; this is + // only a quality-of-service policy. It affects mostly performance, + // but the "best efforts" semantics for "oneway" messages would also + // be impacted in that some (by definition, buggy!) code which used + // only "oneway" messages might not work at all. + // + + // + // Build the outgoing message, starting with generic GIOP header. + // + if (start_message (Request, stream) != CORBA_B_TRUE) { + env.exception (new CORBA_MARSHAL (COMPLETED_NO)); + return; + } + + // + // Then fill in the rest of the RequestHeader + // + // The first element of header is service context list; transactional + // context would be acquired here using the transaction service APIs. + // Other kinds of context are as yet undefined. + // + // Last element of request header is the principal; no portable way + // to get it, we just pass empty principal (convention: indicates + // "anybody"). Steps upward in security include passing an unverified + // user ID, and then verifying the message (i.e. a dummy service context + // entry is set up to hold a digital signature for this message, then + // patched shortly before it's sent). + // + static CORBA_Principal_ptr anybody = 0; + static ServiceContextList svc_ctx; // all zeroes + + if (CDR::encoder (&TC_ServiceContextList, 0, &svc_ctx, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return; + + if (!stream.put_ulong (my_request_id) || !stream.put_boolean (do_rsvp)) { + env.exception (new CORBA_MARSHAL (COMPLETED_NO)); + return; + } + + if (CDR::encoder (&TC_opaque, key, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_String, &opname, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_Principal, &anybody, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return; // right after fault + else + return; // no fault reported +} + +extern CORBA_ExceptionList __system_exceptions; + +// +// Send request, block until any reply comes back, and unmarshal +// reply parameters as appropriate. +// +GIOP::ReplyStatusType +GIOP::Invocation::invoke ( + CORBA_ExceptionList &exceptions, + CORBA_Environment &env +) +{ + // + // Send Request, return on error or if we're done + // + if (!send_message (stream, endpoint->fd)) { + // + // send_message() closed the connection; we just release it here. + // + // XXX highly desirable to know whether we wrote _any_ data; if + // we wrote none, then there's no chance the call completed and + // applications don't have to deal with those nasty indeterminate + // states where they can't immediatly tell if what's safe to do. + // + // XXX also, there might have been a GIOP::CloseConnection message + // in the input queue. If so, this request should be treated as + // a (full) "rebind" case. Can't do that from this point in the + // code however! Some minor restructuring needs to happen. + // + endpoint = 0; + env.exception (new CORBA_COMM_FAILURE (COMPLETED_MAYBE)); + return SYSTEM_EXCEPTION; + } + if (!do_rsvp) + return NO_EXCEPTION; + + // + // This blocks until the response is read. In the current version, + // there is only one client thread that ever uses this connection, so + // most response messages are illegal. + // + // THREADING NOTE: to make more efficient use of connection resources, + // we'd multiplex I/O on connections. For example, one thread would write + // its GIOP::Request (or GIOP::LocateRequest etc) message and block for + // the response, then another would do the same thing. When a response + // came back, it would be handed to the thread which requested it. + // + // Currently the connection manager doesn't support such fine grained + // connection locking, and also this server implementation wouldn't + // take advantage of that potential concurrency in requests either. + // There are often performance losses coming from fine-grained locks + // being used inappropriately; there's some evidence that locking at + // the level of requests loses on at least some platforms. + // + // XXX In all MT environments, there's a cancellation point lurking + // here; need to investigate. Client threads would frequently be + // canceled sometime during read_message ... the correct action to + // take on being canceled is to issue a CancelRequest message to + // the server and then imediately let other client-side cancellation + // handlers do their jobs. + // + // In C++, that basically means to unwind the stack using almost normal + // procedures: all destructors should fire, and some "catch" blocks + // should probably be able to handle things like releasing pointers. + // (Without unwinding the C++ stack, resources that must be freed by + // thread cancellation won't be freed, and the process won't continue + // to function correctly.) The tricky part is that according to POSIX, + // all C stack frames must also have their (explicitly coded) handlers + // called. We assume a POSIX.1c/C/C++ environment. + // + switch (read_message (endpoint->fd, stream, env)) { + case Reply: + // handle reply ... must be right one etc + break; + + case CloseConnection: + // + // Special case of forwarding -- server was closing the connection, + // which just indicates resource constraints, not an error. The + // client is effectively "forwarded" to the same server! + // + // However, we must reinitialize the forwarding chain, since the + // resource being reclaimed might also have been the process, not + // just the connection. Without reinitializing, we'd give false + // error reports to applications. + // + { +#ifdef _POSIX_THREADS + Critical section (&fwd_info_lock); +#endif // _POSIX_THREADS + + delete _data->fwd_profile; + _data->fwd_profile = 0; + + (void) closesocket (endpoint->fd); + endpoint->fd = -1; + endpoint = 0; + return LOCATION_FORWARD; + } + + case Request: + case CancelRequest: + case LocateRequest: + case LocateReply: + default: + // + // These are all illegal messages to find. If found, they could be + // indicative of client bugs (lost track of input stream) or server + // bugs; maybe the request was acted on, maybe not, we can't tell. + // + dmsg ("illegal message in response to my Request!"); + env.exception (new CORBA_COMM_FAILURE (COMPLETED_MAYBE)); + // FALLTHROUGH ... + + case MessageError: + // + // Couldn't read it for some reason ... exception's set already, + // so just tell the other end about the trouble (closing the + // connection) and return. + // + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + + // + // Process reply message. Again, due to the single threading in this + // code, only the reply to this request is allowed to be coming back. + // + // NOTE: if the response really _isn't_ for this thread, it's now + // treated as an error in which synchronization can't be recovered. + // There might be cases where it _could_ be recovered ... e.g. maybe + // for some reason the previous call couldn't pick up its response. + // It'd be worth investigating (and handling) any such cases. + // + // NOTE: since this implementation supports no ORB services (notably, + // the transaction service, which is the only one that's currently + // defined), the reply context is discarded. Normally it'd be fed, + // component at a time, to the relevant services. + // + // NOTE: As security support kicks in, this is the right place to + // verify a digital signature, if that is required in this particular + // runtime security environment. How to know if that's the case? + // It's likely that standard Internet IPSEC infrastructure (RFC 1825 + // through 1827, and successors) will be used to enforce many security + // policies; integrity and privacy guarantees may be provided by the + // network, and need no support here. + // + ServiceContextList reply_ctx; + CORBA_ULong request_id; + CORBA_ULong reply_status; // GIOP::ReplyStatusType + + if (CDR::decoder (&TC_ServiceContextList, &reply_ctx, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + delete reply_ctx.buffer; + + if (!stream.get_ulong (request_id) + || request_id != my_request_id + || !stream.get_ulong (reply_status) + || reply_status > LOCATION_FORWARD) { + send_error (endpoint->fd); + env.exception (new CORBA_COMM_FAILURE (COMPLETED_MAYBE)); + dmsg ("bad Response header"); + return SYSTEM_EXCEPTION; + } + + // + // If there was no exception, let the caller parse the normal response. + // Otherwise parse and handle the response; we always know how to deal + // with the standard exceptions, and the caller provides a list of + // allowed user-defined exceptions so that we know how to unmarshal + // those too (without IFR consultation). + // + // When requests are forwarded, we just store the revised profile data + // in this objref structure. The expectation is that the call will + // be reissued until someone gives up on a forwarding chain, and that + // other calls will reap the benefit of the forwarding work by this + // thread. + // + // NOTE: should ensure that from here on, all system exceptions + // return COMPLETED_YES status ... even ones reported by code which + // we call. + // + switch (reply_status) { + case NO_EXCEPTION: + break; + + case USER_EXCEPTION: + case SYSTEM_EXCEPTION: + { + CORBA_String exception_id; + + // + // Pull the exception ID out of the marshaling buffer. + // + { + CORBA_ULong len; + + // + // Read "length" field of string, so "next" points + // right at the null-terminated ID. Then get the ID. + // + if (stream.get_ulong (len) != CORBA_B_TRUE + || len > stream.remaining) { + send_error (endpoint->fd); + env.exception (new CORBA_MARSHAL (COMPLETED_YES)); + return SYSTEM_EXCEPTION; + } + exception_id = (CORBA_String) stream.next; + stream.skip_bytes (len); + } + + // + // User and system exceptions differ only in what table of + // exception typecodes is searched. + // + CORBA_ExceptionList *xlist; + + if (reply_status == USER_EXCEPTION) + xlist = &exceptions; + else + xlist = &__system_exceptions; + + // + // Find it in the operation description and then use that to get + // the typecode. Use it to unmarshal the exception's value; if + // that exception is not allowed by this operation, fail (next). + // + unsigned i; + CORBA_TypeCode_ptr *tcp; + + for (i = 0, tcp = xlist->buffer; + i < xlist->length; + i++, tcp++) { + CORBA_String xid; + + xid = (*tcp)->id (env); + if (env.exception () != 0) { + dexc (env, "invoke(), get exception ID"); + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + + if (strcmp ((char *)exception_id, (char *)xid) == 0) { + size_t size; + CORBA_Exception *exception; + + size = (*tcp)->size (env); + if (env.exception () != 0) { + dexc (env, "invoke(), get exception size"); + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + + // + // Create the exception, fill in the generic parts + // such as vtable, typecode ptr, refcount ... we + // need to clean them all up together, in case of + // errors unmarshaling. + // + exception = new (new char [size]) CORBA_Exception (*tcp); + + if (CDR::decoder (*tcp, exception, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + delete exception; + dmsg2 ("invoke, unmarshal %s exception %s", + (reply_status == USER_EXCEPTION) + ? "user" : "system", + exception_id); + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + env.exception (exception); + return (GIOP::ReplyStatusType) reply_status; + } + } + + // + // If we couldn't find this exception's typecode, report it as + // an OA error since the skeleton passed an exception that was + // not allowed by the operation's IDL definition. In the case + // of a dynamic skeleton it's actually an implementation bug. + // + // It's known to be _very_ misleading to try reporting this as + // any kind of marshaling error (unless minor codes are made + // to be _very_ useful) ... folk try to find/fix ORB bugs that + // don't exist, not bugs in/near the implementation code. + // + if (reply_status == USER_EXCEPTION) + env.exception (new CORBA_OBJ_ADAPTER (COMPLETED_YES)); + else + env.exception (new CORBA_INTERNAL (COMPLETED_MAYBE)); + return SYSTEM_EXCEPTION; + } + // NOTREACHED + + case LOCATION_FORWARD: + { + CORBA_Object_ptr obj; + IIOP_Object *obj2; + + // + // Unmarshal the object we _should_ be calling. We know + // that one of the facets of this object will be an IIOP + // invocation profile. + // + if (CDR::decoder (_tc_CORBA_Object, &obj, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || obj->QueryInterface (IID_IIOP_Object, (void **)&obj2) + != NOERROR + ) { + dexc (env, "invoke, location forward"); + send_error (endpoint->fd); + return SYSTEM_EXCEPTION; + } + CORBA_release (obj); + + // + // Make a copy of the IIOP profile in the forwarded objref, + // reusing memory where practical. Then delete the forwarded + // objref, retaining only its profile. + // + // XXX add and use a "forward count", to prevent loss of data + // in forwarding chains during concurrent calls -- only a + // forward that's a response to the current fwd_profile should + // be recorded here. (This is just an optimization, and is + // not related to correctness.) + // +#ifdef _POSIX_THREADS + Critical section (&fwd_info_lock); +#endif // _POSIX_THREADS + + delete _data->fwd_profile; + _data->fwd_profile = new IIOP::ProfileBody (obj2->profile); + + obj2->Release (); + + env.clear (); + + // + // Make sure a new connection is used next time. + // + endpoint = 0; + } + break; + } + + // + // All standard exceptions from here on in the call path know for certain + // that the call "completed" ... except in the case of system exceptions + // which say otherwise, and for LOCATION_FORWARD responses. + // + return (GIOP::ReplyStatusType) reply_status; +} + + +// +// Generic server side read + dispatch one message; returns when that +// bit of work is complete. +// +// In the typical case, the request and response buffers live on the +// stack so that the heap never gets used. These grow if needed. +// +void +GIOP::incoming_message ( + int &fd, + LocateStatusType check_forward ( + opaque &key, + CORBA_Object_ptr &objref, + void *context + ), + void handle_request ( + RequestHeader &req, + CDR &req_body, + CDR *reply, + void *context, + CORBA_Environment &env + ), + void *context, + CORBA_Environment &env +) +{ + unsigned char buffer [CDR::DEFAULT_BUFSIZE]; + CDR msg (&buffer [0], sizeof buffer); + + switch (read_message (fd, msg, env)) { + case Request: + { + RequestHeader req; + CORBA_Boolean hdr_status; + + // + // Tear out the service context ... we currently ignore it, + // but it should probably be passed to each ORB service as + // appropriate (e.g. transactions, security). + // + // NOTE: As security support kicks in, this is a good place + // to verify a digital signature, if that is required in this + // security environment. It may be required even when using + // IPSEC security infrastructure. + // + hdr_status = CDR::decoder (&TC_ServiceContextList, + &req.service_info, 0, &msg, env); + + // + // Get the rest of the request header ... + // + hdr_status = hdr_status && msg.get_ulong (req.request_id); + hdr_status = hdr_status && msg.get_boolean (req.response_expected); + hdr_status = hdr_status && CDR::decoder (&TC_opaque, &req.object_key, + 0, &msg, env); + hdr_status = hdr_status && CDR::decoder (_tc_CORBA_String, &req.operation, + 0, &msg, env); + hdr_status = hdr_status && CDR::decoder (_tc_CORBA_Principal, + &req.requesting_principal, 0, &msg, env); + + + // XXX check whether hdr_status identifies a header + // unmarshaling error, and handle appropriately + +#ifdef DEBUG + if (debug_level >= 3) { + dmsg_v ("%sRequest ID %#lx from FD %d", + req.response_expected ? "" : "Oneway ", + req.request_id, fd); + if (debug_level >= 4) { + dmsg_opaque ("object key", req.object_key.buffer, + req.object_key.length); + dmsg_v (" opname '%s'", req.operation); + if (req.requesting_principal) + dmsg_opaque ("client principal", + req.requesting_principal->id.buffer, + req.requesting_principal->id.length); + else + dmsg (" client principal (EMPTY)"); + } + + // NOTE: describe any service context, and how + // many bytes of non-header data were sent. + } +#endif // DEBUG + + // + // Verify that we're to dispatch the request within this + // particular process. + // + if (check_forward != 0) { + LocateStatusType status; + CORBA_Object_ptr fwd_ref = 0; + + status = check_forward (req.object_key, fwd_ref, context); + if (status != OBJECT_HERE) { + ServiceContextList resp_ctx; + unsigned char buf2 [CDR::DEFAULT_BUFSIZE]; + CDR response (&buf2 [0], + sizeof buf2); + + start_message (Reply, response); + resp_ctx.length = 0; + CDR::encoder (&TC_ServiceContextList, &resp_ctx, + 0, &response, env); + response.put_ulong (req.request_id); + + // + // If we're not sending a response, just clean up. + // + if (!req.response_expected) { + if (status == OBJECT_FORWARD) + CORBA_release (fwd_ref); + + // + // Else either forward the request ... + // + } else if (status == OBJECT_FORWARD) { + dmsg ("forwarding Request message"); + response.put_ulong (LOCATION_FORWARD); + CDR::encoder (_tc_CORBA_Object, &fwd_ref, + 0, &response, env); + CORBA_release (fwd_ref); + (void) send_message (response, fd); + + // + // ... or report exception that the object doesn't exist. + // + } else { + CORBA_OBJECT_NOT_EXIST exc (COMPLETED_YES); + + response.put_ulong (SYSTEM_EXCEPTION); + (void) CDR::encoder (_tc_CORBA_OBJECT_NOT_EXIST, + &exc, 0, &response, env); + + (void) send_message (response, fd); + } + + delete req.object_key.buffer; + CORBA_string_free (req.operation); + return; + } + } + + + // + // So, we read a request, now dispatch it using something more + // primitive than a CORBA2 ServerRequest pseudo-object. + // + if (req.response_expected) { + ServiceContextList resp_ctx; + unsigned char buf2 [CDR::DEFAULT_BUFSIZE]; + CDR response (&buf2 [0], sizeof buf2); + + start_message (Reply, response); + resp_ctx.length = 0; + CDR::encoder (&TC_ServiceContextList, &resp_ctx, + 0, &response, env); + response.put_ulong (req.request_id); + + handle_request (req, msg, &response, context, env); + + // + // "handle_request" routine puts ReplyStatusType then + // parameters. + // + (void) send_message (response, fd); + } else + handle_request (req, msg, 0, context, env); + + delete req.object_key.buffer; + CORBA_string_free (req.operation); + } + break; + + // + // Forward requests as needed; if caller hasn't provided code to + // support forwarding, we default to doing no forwarding. + // + case LocateRequest: + { + CORBA_ULong request_id; + opaque key; + + msg.get_ulong (request_id); + CDR::decoder (&TC_opaque, &key, 0, &msg, env); + + // + // we've read the request header; send a LocateReply + // + unsigned char resp [CDR::DEFAULT_BUFSIZE]; + CDR response (resp, sizeof resp); + CORBA_Object_ptr fwd_ref = 0; + + start_message (LocateReply, response); + response.put_ulong (request_id); + if (check_forward == 0) { + response.put_ulong (OBJECT_HERE); + dmsg ("LocateRequest response: object is (always) here!"); + } else { + LocateStatusType status; + + status = check_forward (key, fwd_ref, context); + response.put_ulong ((CORBA_ULong) status); + if (status == OBJECT_FORWARD) { + dmsg ("LocateRequest response: forward requests"); + CDR::encoder (_tc_CORBA_Object, &fwd_ref, 0, + &response, env); + } else if (status == OBJECT_HERE) { + dmsg ("LocateRequest response: object is here!"); + } else { + dmsg ("LocateRequest response: no such object"); + } + } + (void) send_message (response, fd); + } + break; + + // + // Cancel request -- ignored this implementation. + // + // THREADING NOTE: Handling it would require (a) some thread to read + // the CancelRequest while one was working on handling the Request, + // (b) a way to find the thread working on that request, (c) using + // thread cancellation to alert that thread that the work it's + // been doing can safely be discarded. Also of course (d) making + // the various worker threads cleanly handle cancellation, and + // (e) modifying client code to send a CancelRequest when it's + // been canceled. + // + case CancelRequest: + { + CORBA_ULong request_id; + + msg.get_ulong (request_id); + } + break; + + // + // These messages should never be sent to the server; it's an error + // if the peer tries. Set the environment accordingly, as it's not + // yet been reported as an error. + // + case Reply: + case LocateReply: + case CloseConnection: + default: // Unknown message + dmsg ("Illegal message received by server"); + env.exception (new CORBA_COMM_FAILURE (COMPLETED_NO)); + // FALLTHROUGH + + // + // read_message() has already set some error in the environment for + // all "MessageError" cases, so don't clobber it. + // + // General error recovery is to send MessageError to the peer just + // in case (it'll fail on EOF) and then close the connection. + // + case MessageError: + send_error (fd); + break; + } + + // ... error if unconsumed data remains; is this the spot to test that? +} diff --git a/TAO/IIOP/lib/bridge/giop.hh b/TAO/IIOP/lib/bridge/giop.hh new file mode 100644 index 00000000000..daf952ef0e0 --- /dev/null +++ b/TAO/IIOP/lib/bridge/giop.hh @@ -0,0 +1,317 @@ +// @(#)giop.hh 1.2 95/09/06 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// GIOP data structures and support routines +// +// Note that some symbols in this module are part of the "Internet" +// Inter-ORB Protocol (IIOP), not the General IOP. Only addressing +// information and certain details of connection usage are specific +// to IIOP; all other protocol details can be reused by ORB protocols +// that are built atop connection protocols other than TCP. +// +// THREADING NOTE: Threads should never manipulate another thread's +// invocations. In this implementation, all data structures used to +// represent invocations (and parts of them) are owned by the thread +// which created them. Multiple threads may make of course concurrent +// invocations safely, since the GIOP code is reentrant. +// + +#ifndef _GIOP_HH +#define _GIOP_HH + +#include <corba/orb.hh> +#include <corba/stub.hh> + +#include "bridge/connmgr.hh" +#include "bridge/iiopobj.hh" // XXX -- not generic! + + +// XXX this same typedef is used in other places, e.g. iiopobj.hh +typedef CORBA_SEQUENCE <CORBA_Octet> opaque; + +class IOP { // namespace + public: + // + // Assigned Protocol/Profile tag values. ORB protcols may be + // uniquely identified by tags such as these. This allows each + // ORB's own objref profiles to be interchanged using IORs. + // + // Email to tag-request@omg.org to allocate tags + // + typedef CORBA_ULong ProfileId; + enum { + TAG_INTERNET_IOP = 0, // IIOP + TAG_MULTIPLE_COMPONENTS = 1, // DCE-CIOP + + // + // This is a subset of the list of other profile tags + // + TAG_ONC_IOP = 0x4f4e4300 // ONC IOP + }; + + struct TaggedProfile { // one per protocol + ProfileId tag; + opaque profile_data; + }; + typedef CORBA_SEQUENCE <TaggedProfile> TaggedProfileSeq; + + // + // InteroperableObjectReference ... a set of protocol-specific + // protocol profiles, plus a type ID. Only one object is denoted + // by all of this information. It's OK to delete all profiles + // except the one for the single protocol actually being used. + // + struct IOR { + char *type_id; + TaggedProfileSeq profiles; + }; + + // + // Some protocols can be factored into a set of optional components. + // Use of such components is defined by the protocol's specification. + // + // Email to tag-request@omg.org to allocate tags + // + typedef CORBA_ULong ComponentId; + enum { + // + // these are all defined by DCE-CIOP in OMG TC document 95-3-10 + // + TAG_DCE_STRING_BINDING = 100, // string binding handle + TAG_DCE_BINDING_NAME = 101, // CDS/GDS/... name + TAG_DCE_NO_PIPES = 102, // no component data + TAG_OBJECT_KEY = 10, // opaque + TAG_ENDPOINT_ID = 11, // uuid + TAG_LOCATION_POLICY = 12 // octet/enum + }; + + // + // One way to represent multicomponent profiles, e.g. as done by + // the DCE-CIOP protocol. One of these gets encapsulated in + // TaggedProfile::profile_data. TAG_MULTIPLE_COMPONENTS may be + // used to represent protocol profiles structured in that way, + // but protocol-specific tags facilitate simpler scanning of IORs + // since you can be assured that each profile only has data used + // within a single ORB protocol. + // + struct TaggedComponent { + ComponentId tag; + opaque component_data; + }; + typedef CORBA_SEQUENCE <TaggedComponent> MultipleComponentProfile; +}; + + +class GIOP { // namespace + + public: + struct Version { CORBA_Octet major, minor; }; + + // + // GIOP protocol version information + // + enum { MY_MAJOR = 1, MY_MINOR = 0 }; // 1.0 + + // + // All GIOP messages include a header and message type. + // + enum MsgType { + Request = 0, // sent by client + Reply = 1, // by server + CancelRequest = 2, // by client + LocateRequest = 3, // by client + LocateReply = 4, // by server + CloseConnection = 5, // by server + MessageError = 6 // by both + }; + + struct MessageHeader { + CORBA_Char magic [4]; // "GIOP" + Version giop_version; + CORBA_Octet byte_order; // 0 = big, 1 = little + CORBA_Octet message_type; // MsgType above + CORBA_ULong message_size; // in byte_order! + }; + + // + // Support for Implicit ORB Service Context + // + typedef CORBA_ULong ServiceID; + enum { + TransactionService = 0 + // + // more service IDs may be defined by OMG + // + }; + struct ServiceContext { + ServiceID context_id; + opaque context_data; + }; + typedef CORBA_SEQUENCE <ServiceContext> ServiceContextList; + + // + // Request, Reply headers + // + struct RequestHeader { + ServiceContextList service_info; + CORBA_ULong request_id; + CORBA_Boolean response_expected; + opaque object_key; + CORBA_String operation; + CORBA_Principal_ptr requesting_principal; + }; + + enum ReplyStatusType { + NO_EXCEPTION, + USER_EXCEPTION, + SYSTEM_EXCEPTION, + LOCATION_FORWARD + }; + + struct ReplyHeader { + ServiceContextList service_info; + CORBA_ULong request_id; + ReplyStatusType reply_status; + }; + + // + // Cancellation -- applies both to Requests and LocateRequests. + // + struct CancelRequestHeader { + CORBA_ULong request_id; + }; + + // + // Location service support + // + struct LocateRequestHeader { + CORBA_ULong request_id; + opaque object_key; + }; + + enum LocateStatusType { + UNKNOWN_OBJECT, + OBJECT_HERE, + OBJECT_FORWARD + }; + + struct LocateReplyHeader { + CORBA_ULong request_id; + LocateStatusType locate_status; + }; + + + // + // Invocation: Sends a Request, optionally reads associated Reply. + // Uses transport info passed in, doesn't locate anything. + // + class Invocation { + public: + Invocation ( + IIOP_Object *data, + const char *operation, + CORBA_Boolean is_roundtrip + ); + ~Invocation (); + + // + // "start" goes beyond initialising data structures, and + // makes calls that may fail -- and thus throw exceptions. + // + void start ( + CORBA_Environment &env + ); + + void put_param ( + CORBA_TypeCode_ptr tc, + void *value, + CORBA_Environment &env + ) + { + (void) CDR::encoder (tc, value, 0, &stream, env); + } + + ReplyStatusType invoke ( + CORBA_ExceptionList &exceptions, + CORBA_Environment &env + ); + + void get_value ( + CORBA_TypeCode_ptr tc, + void *value, + CORBA_Environment &env + ) + { + (void) CDR::decoder (tc, value, 0, &stream, env); + } + + // no CORBA_Context support (deprecated) + + private: + IIOP_Object *_data; + const char *opname; + CORBA_Boolean do_rsvp; + CORBA_ULong my_request_id; + + unsigned char buffer [CDR::DEFAULT_BUFSIZE]; + CDR stream; + + autorelease <client_endpoint> endpoint; + }; + + // + // Close a connection, first sending GIOP::CloseConnection + // + static void close_connection (int &fd, void *ctx); + + // + // Generic server side data dispatch -- called for all file descriptors + // on which incoming messages are expected. + // + // The handle_request() routine is used to handle request messages; its + // 'reply' parameter is null if the request is "oneway" (or the client + // isn't waiting for the response that this request normally creates). + // + // The optional check_forward() routine is used to verify that the + // request is to be delivered within this process by handle_request(). + // Each call to handle_request() is preceded by a call to this routine + // if it's provided. It's used when handling GIOP "Request" messages + // as well as GIOP "LocateRequest" messages, and returns an enum to + // indicate overal status (LocateStatusType) as well as an objref + // in the case of OBJECT_FORWARD. That objref is released. + // + static void incoming_message ( + int &fd, + LocateStatusType check_forward ( + opaque &key, + CORBA_Object_ptr &objref, + void *context + ), + void handle_request ( + RequestHeader &req, + CDR &request_body, + CDR *reply, + void *context, + CORBA_Environment &env + ), + void *context, + CORBA_Environment &env + ); + + static CORBA_Boolean send_message ( + CDR &stream, + int &connection + ); + + // + // Reads message, returns message type from header + // + static MsgType read_message ( + int &connection, + CDR &msg, + CORBA_Environment &env + ); +}; + +#endif // _GIOP_HH diff --git a/TAO/IIOP/lib/bridge/iiopobj.cpp b/TAO/IIOP/lib/bridge/iiopobj.cpp new file mode 100644 index 00000000000..ba108478c72 --- /dev/null +++ b/TAO/IIOP/lib/bridge/iiopobj.cpp @@ -0,0 +1,198 @@ +// @(#)iiopobj.cpp 1.9 95/11/04 +// Copyright 1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP Bridge: CORBA::Object operations +// +// Some CORBA::Object and other operations are specific to this IIOP +// based implementation, and can neither be used by other kinds of objref +// nor have a default implementation. +// + +#include <assert.h> +#include <limits.h> +#include <corba/orb.hh> + +#include <initguid.h> +#include <string.h> + +#include <corba/stub.hh> + +#include "runtime/thread.hh" + +#include "bridge/iiopobj.hh" + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t iiopobj_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + + +IIOP::ProfileBody::ProfileBody ( + const IIOP::ProfileBody &src +) : + iiop_version (src.iiop_version), + port (src.port) +{ + assert (src.iiop_version.major == MY_MAJOR); + assert (src.iiop_version.minor == MY_MINOR); + + host = strdup (src.host); + + object_key.length = object_key.maximum = src.object_key.length; + object_key.buffer = (CORBA_Octet *) malloc (object_key.maximum); + (void) memcpy (object_key.buffer, src.object_key.buffer, + object_key.length); +} + + +// +// Quick'n'dirty hash of objref data, for partitioning objrefs into sets +// +// NOTE that this must NOT go across the network! +// +CORBA_ULong +IIOP_Object::hash ( + CORBA_ULong max, + CORBA_Environment &env +) +{ + CORBA_ULong hashval; + + env.clear (); + + // + // Just grab a bunch of convenient bytes and hash them; could do + // more (hostname, full key, exponential hashing) but no real need + // to do so except if performance requires a more costly hash. + // + hashval = profile.object_key.length * profile.port; + hashval += profile.iiop_version.minor; + if (profile.object_key.length >= 4) { + hashval += profile.object_key.buffer [1]; + hashval += profile.object_key.buffer [3]; + } + + return hashval % max; +} + + +// +// Expensive comparison of objref data, to see if two objrefs certainly +// point at the same object. (It's quite OK for this to return FALSE, +// and yet have the two objrefs really point to the same object.) +// +// NOTE that this must NOT go across the network! +// +CORBA_Boolean +IIOP_Object::is_equivalent ( + CORBA_Object_ptr other_obj, + CORBA_Environment &env +) +{ + IIOP::ProfileBody *body, *body2; + IIOP_Object *other_iiop_obj; + + env.clear (); + + if (CORBA_is_nil (other_obj) == CORBA_B_TRUE + || other_obj->QueryInterface (IID_IIOP_Object, + (void **)&other_iiop_obj) != NOERROR) + return CORBA_B_FALSE; + CORBA_release (other_obj); + + // + // Compare all the bytes of the object address -- must be the same + // + body = &profile; + body2 = &other_iiop_obj->profile; + + assert (body->object_key.length < UINT_MAX); + + return body->object_key.length == body2->object_key.length + && memcmp (body->object_key.buffer, body2->object_key.buffer, + (size_t) body->object_key.length) == 0 + && body->port == body2->port + && strcmp ((char *)body->host, (char *)body2->host) == 0 + && body->iiop_version.minor == body2->iiop_version.minor + && body->iiop_version.major == body2->iiop_version.major; +} + + +// +// For COM -- IUnknown operations +// + +// {A201E4C3-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_IIOP_Object, +0xa201e4c3, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +IIOP_Object::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&iiopobj_lock); +#endif + + return ++_refcount; +} + +ULONG +__stdcall +IIOP_Object::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&iiopobj_lock); +#endif + + if (--_refcount != 0) + return _refcount; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + delete this; + return 0; +} + + +// +// Note that (as of this writing) this is the only place all +// the interfaces to an "objref" come together: +// +// IUnknown ... this one +// STUB_OBJECT ... inherited by this one +// IIOP_OBJECT ... this one +// +// CORBA_Object ... contained within this; it delegates back +// to this one as its "parent" +// +HRESULT +__stdcall +IIOP_Object::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_IIOP_Object == riid + || IID_STUB_Object == riid + || IID_IUnknown == riid) + *ppv = this; + else if (IID_CORBA_Object == riid) + *ppv = &base; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} diff --git a/TAO/IIOP/lib/bridge/iiopobj.hh b/TAO/IIOP/lib/bridge/iiopobj.hh new file mode 100644 index 00000000000..1ca702b23b3 --- /dev/null +++ b/TAO/IIOP/lib/bridge/iiopobj.hh @@ -0,0 +1,154 @@ +// @(#)iiopobj.hh 1.9 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP objref representation +// +// This allows stubs which support multiple protocols, since the +// stub (and DII) code only work with the parent "STUB_Objref" +// class when making calls. +// + +#ifndef _iiopobj_hh +#define _iiopobj_hh + +typedef CORBA_SEQUENCE <CORBA_Octet> opaque; + +class _EXPCLASS IIOP +{ // namespace + public: + + struct Version { CORBA_Octet major, minor; }; + + // + // IIOP Protocol version is distinct from GIOP version. + // + enum { MY_MAJOR = 1, MY_MINOR = 0 }; + + // + // IOR support ... ProfileBody is encapsulated in an IIOP + // profile entry within an IOR. + // + struct ProfileBody { + Version iiop_version; + CORBA_String host; + CORBA_UShort port; + opaque object_key; + + ProfileBody () + : host (0) { } + + ProfileBody (const ProfileBody &src); + + ~ProfileBody () + { delete host; delete object_key.buffer; } + private: + ProfileBody &operator = (const ProfileBody &src); + }; +}; + + +// +// Representation of an IIOP objref: the profile body, and any +// forwarded pointer. Implementations of basic invocation code; +// how to marshal an objref. Contains a CORBA::Object interface. +// +// NOTE that this uses (single) implementation inheritance to share +// most of the basic code for an object reference. +// +extern "C" const IID IID_IIOP_Object; + +class _EXPCLASS IIOP_Object : public STUB_Object +{ + public: + // + // stub-based invocation + // + void do_call ( + CORBA_Environment &env, + const calldata *info, + ... + ); + + // + // DII based invocation + // + void do_dynamic_call ( + const char *opname, + CORBA_Boolean is_roundtrip, + CORBA_NVList_ptr args, + CORBA_NamedValue_ptr result, + CORBA_Flags flags, + CORBA_ExceptionList &exceptions, + CORBA_Environment &env + ); + + // + // Support for tables keyed by objrefs. + // + CORBA_ULong hash ( + CORBA_ULong maximum, + CORBA_Environment &env + ); + CORBA_Boolean is_equivalent ( + CORBA_Object_ptr other_obj, + CORBA_Environment &env + ); + + // + // XXX All objref representations should know how to marshal themselves. + // That will involve ensuring that the IOR that gets marshaled talks a + // specific protocol, otherwise the target of a message would not be + // invoke using the objref it receives (compromising functionality in + // a very basic and mysterious mannter). So for example an objref might + // need to create a proxy for itself rather than marshaling its own + // representation. [ The IIOP engine does not need to worry about such + // issues since it only supports one protocol -- the problem won't show + // up. "Multiprotocol ORBs" will need to solve that problem though. ] + // + + IIOP::ProfileBody profile; + IIOP::ProfileBody *fwd_profile; + + IIOP_Object (char *repository_id) + : fwd_profile (0), base (this), + STUB_Object (repository_id), + _refcount (1) + { } + + // + // COM stuff + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID type_id, + void **ppv + ); + + private: + CORBA_Object base; + unsigned _refcount; + + // + // Destructor is to be called only through Release() + // + ~IIOP_Object () + { assert (_refcount == 0); + delete fwd_profile; } + + // + // Disallow copy constructor and assignment operator + // + IIOP_Object (const IIOP_Object &); + operator = (const IIOP_Object &); +#if defined (__GNUG__) + // + // G++ (even 2.6.3) stupidly thinks instances can't be + // created. This de-warns. + // + friend class everyone_needs_a_friend; +#endif +}; + +#endif // _iiopobj_hh diff --git a/TAO/IIOP/lib/bridge/iioporb.cpp b/TAO/IIOP/lib/bridge/iioporb.cpp new file mode 100644 index 00000000000..719c3ccc943 --- /dev/null +++ b/TAO/IIOP/lib/bridge/iioporb.cpp @@ -0,0 +1,408 @@ +// @(#)iioporb.cpp 1.8 95/09/19 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP: ORB pseudo-object +// +// This includes objref stringification/destringification for IIOP object +// references. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <initguid.h> +#include <corba/stub.hh> + +#include "runtime/cdr.hh" +#include "bridge/iioporb.hh" +#include "bridge/iiopobj.hh" + + +static const char ior_prefix [] = "IOR:"; +static const char iiop_prefix [] = "iiop:"; +static const char xchars [] = "0123456789abcdef"; + + +// +// hex conversion utilities +// +static inline char +nibble2hex (unsigned n) +{ + return xchars [n & 0x0f]; +} + +static inline +unsigned char +hex2byte (char c) +{ + if (isdigit (c)) + return (unsigned char) (c - '0'); + else if (islower (c)) + return (unsigned char) (10 + c - 'a'); + else + return (unsigned char) (10 + c - 'A'); +} + + +// +// Objref stringification +// +CORBA_String +IIOP_ORB::object_to_string ( + CORBA_Object_ptr obj, + CORBA_Environment &env +) +{ + env.clear (); + + // + // Application writer controls what kind of objref strings they get, + // maybe along with other things, by how they initialize the ORB. + // + if (use_omg_ior_format) { + + // + // By default, orbs use IOR strings; these are ugly (and error + // prone) but specified by CORBA. + // + // XXX there should be a simple way to reuse this code in other + // ORB implementations ... + // + unsigned char *bytes, buf [BUFSIZ]; + CDR cdr (buf, sizeof buf, MY_BYTE_SEX); + + bytes = buf; + (void) memset (bytes, 0, BUFSIZ); // support limited oref strcmp + + // + // Marshal the objref into an encapsulation bytestream. + // + (void) cdr.put_char (MY_BYTE_SEX); + if (CDR::encoder (_tc_CORBA_Object, &obj, 0, &cdr, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return 0; + + // + // Now hexify the encapsulated CDR data into a string, + // and return that string. + // + CORBA_String string, cp; + size_t len = cdr.length - cdr.remaining; + + string = CORBA_string_alloc (sizeof ior_prefix + 2 * len); + strcpy ((char *)string, ior_prefix); + + for (cp = (CORBA_String) strchr ((char *)string, ':') + 1, + bytes = cdr.buffer; + len--; bytes++) { + *cp++ = nibble2hex ((*bytes) >> 4); + *cp++ = nibble2hex (*bytes); + } + *cp = 0; + return string; + } else { + // + // The "internet" ORB uses readable URL style objrefs, as + // used in the World Wide Web. + // + // NOTE: the version ID in the string is ugly but we can't + // realistically eliminate it by any "assume 1.0" strategy... + // Similarly with the port, because there's no single IIOP + // port to which we could default. + // + static const char digits [] = "0123456789"; + + // + // This only works for IIOP objrefs. If we're handed an objref + // that's not an IIOP objref, fail -- application must use an + // ORB that's configured differently. + // + IIOP_Object *obj2; + + if (obj->QueryInterface (IID_IIOP_Object, (void **)&obj2) + != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return 0; + } + + if (!obj2) // null? + return CORBA_string_copy ((CORBA_String) iiop_prefix); + + char buf [BUFSIZ + 2]; + + sprintf (buf, "%s%c.%c//%s:%d/", iiop_prefix, + digits [obj2->profile.iiop_version.major], + digits [obj2->profile.iiop_version.minor], + obj2->profile.host, obj2->profile.port); + + char *cp = strchr (buf, 0); + unsigned len; + unsigned char *byte; + + for (len = (unsigned) obj2->profile.object_key.length, + byte = obj2->profile.object_key.buffer; + cp < &buf [BUFSIZ] && len != 0; + len--, byte++) { + if (isascii (*byte) && isprint (*byte) && *byte != '\\') { + *cp++ = (char) *byte; + continue; + } + + // + // NOTE: this could run two characters past &buf[BUFSIZ], + // which is why buf is exactly two characters bigger than + // that ... saves coding a test here. + // + *cp++ = '\\'; + *cp++ = nibble2hex (*byte & 0x0f); + *cp++ = nibble2hex ((*byte >> 4) & 0x0f); + } + if (cp >= &buf [BUFSIZ]) { + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + return 0; + } + *cp = 0; + return CORBA_string_copy ((CORBA_String) &buf[0]); + } +} + + +// +// Destringify OMG-specified "IOR" string. +// +// XXX there should be a simple way to reuse this code in other +// ORB implementations ... +// +static CORBA_Object_ptr +ior_string_to_object ( + CORBA_String str, + CORBA_Environment &env +) +{ + // + // Unhex the bytes, and make a CDR deencapsulation stream + // from the resulting data. + // + unsigned char *buffer = new + unsigned char [1 + strlen ((char *) str) / 2]; + char *tmp = (char *)str; + size_t len = 0; + + while (tmp [0] && tmp [1]) { + unsigned char byte; + + if (!(isxdigit (tmp [0]) && isxdigit (tmp [1]))) + break; + + byte = (unsigned char) (hex2byte (tmp [0]) << 4); + byte |= hex2byte (tmp [1]); + + buffer [len++] = byte; + tmp += 2; + } + if (tmp [0] && !isspace (tmp [0])) { + delete buffer; + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; + } + + // + // Create deencapsulation stream ... then unmarshal objref + // from that stream. + // + CDR stream; + CORBA_Object_ptr objref; + + stream.setup_encapsulation (buffer, len); + if (CDR::decoder (_tc_CORBA_Object, &objref, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + objref = 0; + } + + delete buffer; + return objref; +} + + +// +// Destringify URL style IIOP objref. +// +static CORBA_Object_ptr +iiop_string_to_object ( + CORBA_String string, + CORBA_Environment &env +) +{ + // + // NIL objref encodes as just "iiop:" ... which has already been + // removed, so we see it as an empty string. + // + if (!string || !*string) + return 0; + + // + // type ID not encoded in this string ... makes narrowing rather + // expensive, though it does ensure that type-safe narrowing code + // gets thoroughly excercised/debugged! + // + IIOP_Object *data = new IIOP_Object (0); // null type ID + + // + // Remove the "N.N//" prefix, and verify the version's one + // that we accept + // + if (isdigit (string [0]) && isdigit (string [2]) && string [1] == '.' + && string [3] == '/' && string [4] == '/') { + data->profile.iiop_version.major = (char) (string [0] - '0'); + data->profile.iiop_version.minor = (char) (string [2] - '0'); + string += 5; + } else { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + data->Release (); + return 0; + } + if (data->profile.iiop_version.major != IIOP::MY_MAJOR + || data->profile.iiop_version.minor > IIOP::MY_MINOR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + data->Release (); + return 0; + } + + // + // Pull off the "hostname:port/" part of the objref + // + char *cp; + + if ((cp = strchr (string, ':'))== 0) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + data->Release (); + return 0; + } + + data->profile.host = CORBA_string_alloc (1 + cp - string); + for (cp = data->profile.host; *string != ':'; *cp++ = *string++) + continue; + *cp = 0; + string++; + + if ((cp = strchr ((char *)string, '/')) == 0) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + CORBA_string_free (data->profile.host); + data->Release (); + return 0; + } + + data->profile.port = (short) atoi ((char *)string); + string = ++cp; + + // + // Parse the key ... it's ASCII plus hex escapes for everything + // nonprintable. This assumes that printable ASCII is the common + // case ... but since stringification is uncommon, no big deal. + // + data->profile.object_key.buffer + = (unsigned char *) CORBA_string_copy (string); + + // + // Strip out whitespace and adjust length accordingly. + // + for (cp = (char *) data->profile.object_key.buffer; *cp; cp++) { + if (!isprint (*cp)) { + *cp = '\0'; + break; + } + } + string = (char *) data->profile.object_key.buffer; + data->profile.object_key.length = strlen (string); + data->profile.object_key.maximum = data->profile.object_key.length; + + // + // Strip out hex escapes and adjust the key's length appropriately. + // + while ((cp = + strchr ((char *)data->profile.object_key.buffer, '\\')) != 0) { + *cp = (CORBA_Char) (hex2byte ((char) cp [1]) << 4); + *cp |= (CORBA_Char) hex2byte ((char) cp [2]); + cp++; + + size_t len = strlen (cp); + + memcpy (cp, cp+2, len - 2); + data->profile.object_key.length -= 2; + } + + // + // Return the objref. + // + CORBA_Object_ptr obj; + + (void) data->QueryInterface (IID_CORBA_Object, (void **)&obj); + data->Release (); + return obj; +} + + +// +// Destringify arbitrary objrefs. +// +CORBA_Object_ptr +IIOP_ORB::string_to_object ( + CORBA_String str, + CORBA_Environment &env +) +{ + env.clear (); + + // + // Use the prefix code to choose which destringify algorithm to use. + // + if (strncmp ((char *)str, iiop_prefix, sizeof iiop_prefix - 1) == 0) + return iiop_string_to_object (str + sizeof iiop_prefix - 1, env); + + if (strncmp ((char *)str, ior_prefix, sizeof ior_prefix - 1) == 0) + return ior_string_to_object (str + sizeof ior_prefix - 1, env); + + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; +} + +// +// COM IUnknown support +// + +// {A201E4C4-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_IIOP_ORB, +0xa201e4c4, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +HRESULT +__stdcall +IIOP_ORB::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_ORB == riid + || IID_IIOP_ORB == riid + || IID_IUnknown == riid) + *ppv = this; + + // + // XXX gotta aggregate ... + // + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} diff --git a/TAO/IIOP/lib/bridge/iioporb.hh b/TAO/IIOP/lib/bridge/iioporb.hh new file mode 100644 index 00000000000..87130929c28 --- /dev/null +++ b/TAO/IIOP/lib/bridge/iioporb.hh @@ -0,0 +1,49 @@ +// @(#)iioporb.hh 1.2 95/11/04 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// IIOP: bridge implementation of IIOP_ORB +// +// This is one of the two main hooks into IIOP in this Win32 C/C++/COM +// based implementation; the other being IIOP_Object. +// + +#ifndef _iioporb_hh +#define _iioporb_hh +typedef class IIOP_ORB *IIOP_ORB_ptr; + +extern const IID IID_IIOP_ORB; + +// +// ORB pseudo-objref +// +class _EXPCLASS IIOP_ORB : public CORBA_ORB +{ + public: + IIOP_ORB (CORBA_Boolean flag) + { use_omg_ior_format = flag; } + + ~IIOP_ORB () { } + + CORBA_Object_ptr string_to_object ( + CORBA_String str, + CORBA_Environment &env + ); + CORBA_String object_to_string ( + CORBA_Object_ptr obj, + CORBA_Environment &env + ); + + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + private: + CORBA_Boolean use_omg_ior_format; + + // these are not provided + IIOP_ORB (const IIOP_ORB &); + IIOP_ORB &operator = (const IIOP_ORB &); +}; + +#endif // _iioporb_hh diff --git a/TAO/IIOP/lib/bridge/invoke.cpp b/TAO/IIOP/lib/bridge/invoke.cpp new file mode 100644 index 00000000000..fd287d1888d --- /dev/null +++ b/TAO/IIOP/lib/bridge/invoke.cpp @@ -0,0 +1,346 @@ +// @(#)invoke.cpp 1.3 95/09/24 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP stub support for static and dynamic invocation +// +// This file holds DII support, and an analagous interpreter that let static +// stubs be very small. It's specific to objrefs with IIOP::ProfileBody. +// +// NOTE: this may someday be moved within an IIOP class so that the public +// stub interface is completely independent of ORB/protocol internals. +// +// THREADING NOTE: Code below this point is of course thread-safe (at least +// on supported threaded platforms), so the caller of these routines need +// only ensure that the data being passed in is not being modified by any +// other thread. +// +// As an _experiment_ (to estimate the performance cost) remote calls are +// currently deemed "cancel-safe". That means that they can be called +// by threads when they're in asynchronous cancellation mode. The only +// effective way to do this is to disable async cancellation for the +// duration of the call. There are numerous rude interactions with code +// generators for C++ ... cancellation handlers just do normal stack +// unwinding like exceptions, but exceptions are purely synchronous and +// sophisticated code generators rely on that to generate better code, +// which in some cases may be very hard to unwind. +// + +#include <assert.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/cdr.hh" +#include "runtime/thread.hh" +#include "runtime/debug.hh" +#include "bridge/giop.hh" +#include "bridge/connmgr.hh" + + +// +// "stub interpreter" for static stubs. IDL compiler (or human equivalent +// thereof :-) should just dump a read-only description of the call into +// "calldata" and do varargs calls to this routine, which does all the work. +// +// NOTE: This routine includes stub interpreter code, upon which a patent +// application is pending. +// +void +IIOP_Object::do_call ( + CORBA_Environment &env, // exception reporting + const calldata *info, // call description + ... // ... any parameters +) +{ +#ifdef _POSIX_THREADS + ForceSynchronousCancel temp; // side effect driven +#endif // _POSIX_THREADS + + GIOP::Invocation call (this, info->opname, + info->is_roundtrip); + + // + // We may need to loop through here more than once if we're forwarded + // to some other object reference. + // + // NOTE: A quality-of-service policy may be useful to establish here, + // specifically one controlling how many times the call is reissued + // before failing the call on the assumption that something is broken. + // + // NOTE: something missing is a dynamic way to change the policy of + // whether to issue LocateRequest messages or not. This code uses a + // simple, fixed policy: never use LocateRequest messages. + // + for (;;) { + // + // Start the call by constructing the request message header. + // + env.clear (); + call.start (env); + if (env.exception ()) { + dexc (env, "do_call, start request message"); + return; + } + + // + // Now, put all "in" and "inout" parameters into the request + // message body. + // + // Some "inout" data have an extra level of indirection, specified + // by the language mapping's memory allocation policies ... the + // indirection only shows up here when it's needed later for + // allocating "out" memory, otherwise there's just one indirection. + // + unsigned i; + const paramdata *pdp; + va_list param_vector; + + va_start (param_vector, info); + for (i = 0, pdp = info->params; + i < info->param_count; + i++, pdp++) { + void *ptr = va_arg (param_vector, void *); + + if (pdp->mode == PARAM_IN) + call.put_param (pdp->tc, ptr, env); + else if (pdp->mode == PARAM_INOUT) { + if (pdp->value_size == 0) + call.put_param (pdp->tc, ptr, env); + else + call.put_param (pdp->tc, *(void **)ptr, env); + } + + if (env.exception ()) { + dexc (env, "do_call, put request parameter"); + return; + } + } + va_end (param_vector); + + // + // Make the call ... blocking for response if needed. Note that + // "oneway" calls can't return any exceptions except system ones. + // + GIOP::ReplyStatusType status; + CORBA_ExceptionList exceptions; + + exceptions.length = exceptions.maximum = info->except_count; + exceptions.buffer = (CORBA_TypeCode_ptr *) info->excepts; + + status = call.invoke (exceptions, env); + + exceptions.buffer = 0; // don't free it + + if (env.exception ()) { + dexc (env, "do_call, invoke"); + return; + } + if (!info->is_roundtrip + || status == GIOP::SYSTEM_EXCEPTION + || status == GIOP::USER_EXCEPTION) + return; + + // + // Now, get all the "return", "out", and "inout" parameters from + // the response message body. + // + if (status == GIOP::NO_EXCEPTION) { + va_start (param_vector, info); + for (i = 0, pdp = info->params; + i < info->param_count; + i++, pdp++) { + void *ptr = va_arg (param_vector, void *); + + if (pdp->mode == PARAM_RETURN + || pdp->mode == PARAM_OUT + || pdp->mode == PARAM_INOUT) { + // + // The language mapping's memory allocation policy says + // that some data is heap-allocated. This interpreter + // is told about the relevant policy by whoever built + // the operation description (e.g. the IDL compiler) so + // it doesn't have to know the policy associated with a + // particular language binding (e.g. C/C++ differ, and + // C++ even has different policies for different kinds + // of structures). + // + if (pdp->value_size == 0) { + call.get_value (pdp->tc, ptr, env); + } else { + // assert (value_size == tc->size()); + *(void **)ptr = new CORBA_Octet [pdp->value_size]; + call.get_value (pdp->tc, *(void **)ptr, env); + } + + if (env.exception ()) { + dexc (env, "do_call, get reply parameter"); + return; + } + } + } + va_end (param_vector); + return; + } + + // + // ... or maybe this request got forwarded to someplace else; send + // the request there instead. + // + assert (status == GIOP::LOCATION_FORWARD); + } +} + + +// +// DII analogue of the above. Differs in how the vararg calling convention +// is implemented -- DII doesn't use the normal call stack with its implicit +// typing, but iinstead uses heap-based arguments with explicit typing. +// +void +IIOP_Object::do_dynamic_call ( + const char *opname, // operation name + CORBA_Boolean is_roundtrip, // results required? + CORBA_NVList_ptr args, // parameters + CORBA_NamedValue_ptr result, // result + CORBA_Flags flags, // per-call flag (one!) + CORBA_ExceptionList &exceptions, // possible user exceptions + CORBA_Environment &env // exception reporting +) +{ +#ifdef _POSIX_THREADS + ForceSynchronousCancel temp; // side effect driven +#endif // _POSIX_THREADS + + GIOP::Invocation call (this, opname, is_roundtrip); + + // + // Loop as needed for forwarding; see above. + // + for (;;) { + // + // Start the call by constructing the request message header. + // + env.clear (); + call.start (env); + if (env.exception ()) { + dexc (env, "do_call, start request message"); + return; + } + + // + // Now, put all "in" and "inout" parameters into the request + // message body. + // + unsigned i; + + for (i = 0; i < args->count (); i++) { + CORBA_NamedValue_ptr value = args->item (i); + + if (value->flags () == CORBA_ARG_IN + || value->flags () == CORBA_ARG_INOUT) { + call.put_param (value->value ()->type (), + value->value ()->value (), env); + if (env.exception ()) { + dexc (env, "do_dynamic_call, put request parameter"); + return; + } + } + } + + // + // Make the call ... blocking for response if needed. Note that + // "oneway" calls can't return any exceptions except system ones. + // + GIOP::ReplyStatusType status; + + status = call.invoke (exceptions, env); + if (env.exception ()) { + dexc (env, "do_dynamic_call, invoke"); + return; + } + if (!is_roundtrip + || status == GIOP::SYSTEM_EXCEPTION + || status == GIOP::USER_EXCEPTION) + return; + + // + // Now, get all the "return", "out", and "inout" parameters from the + // response message body ... return parameter is first, the rest are + // in the order defined in the IDL spec (which is also the order that + // DII users are required to use). + // + if (status == GIOP::NO_EXCEPTION) { + if (result != 0) { + + // + // If caller didn't set OUT_LIST_MEMORY flag, allocate + // memory for return value ... + // + if (!(flags & CORBA_OUT_LIST_MEMORY)) { + CORBA_TypeCode_ptr tcp; + size_t size; + + tcp = result->value ()->type (); + size = tcp->size (env); + dexc (env, "do_dynamic_call, get result size"); + + if (size != 0) { + void *ptr = new CORBA_Octet [size]; + + tcp->AddRef (); + result->value ()->replace (tcp, ptr, + CORBA_B_TRUE, env); + dexc (env, "do_dynamic_call, set result mem"); + } + } + + call.get_value (result->value ()->type (), + result->value ()->value (), env); + } + + for (i = 0; i < args->count (); i++) { + CORBA_NamedValue_ptr value = args->item (i); + + if (value->flags () == CORBA_ARG_OUT + || value->flags () == CORBA_ARG_INOUT) { + // + // If caller didn't set OUT_LIST_MEMORY flag, allocate + // memory for this parameter ... + // + if (!(flags & CORBA_OUT_LIST_MEMORY)) { + CORBA_TypeCode_ptr tcp; + size_t size; + + tcp = value->value ()->type (); + size = tcp->size (env); + dexc (env, "do_dynamic_call, get param size"); + + if (size != 0) { + void *ptr = new CORBA_Octet [size]; + + tcp->AddRef (); + value->value ()->replace (tcp, ptr, + CORBA_B_TRUE, env); + dexc (env, "do_dynamic_call, set result mem"); + } + } + + call.get_value (value->value ()->type (), + value->value ()->value (), env); + if (env.exception ()) { + dexc (env, "do_dynamic_call, get response parameter"); + return; + } + } + } + return; + } + + // + // ... or maybe this request got forwarded to someplace else. + // + assert (status == GIOP::LOCATION_FORWARD); + } +} diff --git a/TAO/IIOP/lib/bridge/svrrqst.cpp b/TAO/IIOP/lib/bridge/svrrqst.cpp new file mode 100644 index 00000000000..3e895155404 --- /dev/null +++ b/TAO/IIOP/lib/bridge/svrrqst.cpp @@ -0,0 +1,261 @@ +// @(#)svrrqst.cpp 1.9 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Implementation of the Dynamic Server Skeleton Interface +// +#include <corba/orb.hh> + +#include <initguid.h> + +#include "runtime/debug.hh" +#include "runtime/thread.hh" +#include "runtime/cdr.hh" + +#include "bridge/svrrqst.hh" + + +// {77420086-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_IIOP_ServerRequest, +0x77420086, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + +// {4B48D881-F7F0-11ce-9598-0000C07CA898} +DEFINE_GUID(IID_CORBA_ServerRequest, +0x4b48d881, 0xf7f0, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + + +#ifdef _POSIX_THREADS +static pthread_mutex_t svrqst_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + +IIOP_ServerRequest::~IIOP_ServerRequest () +{ + assert (_refcount == 0); + if (_params) + CORBA_release (_params); + if (_retval) + delete _retval; + if (_exception) + delete _exception; +} + + +ULONG +__stdcall +IIOP_ServerRequest::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&svrqst_lock); +#endif + + assert (_refcount > 0); + return _refcount++; +} + +ULONG +__stdcall +IIOP_ServerRequest::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&svrqst_lock); +#endif + + assert (this != 0); + assert (_refcount > 0); + + if (--_refcount != 0) + return _refcount; + + delete this; + return 0; +} + +HRESULT +__stdcall +IIOP_ServerRequest::QueryInterface ( + REFIID riid, + void **ppv +) +{ + assert (_refcount > 0); + *ppv = 0; + + if (IID_IIOP_ServerRequest == riid + || IID_CORBA_ServerRequest == riid + || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + + +// +// Unmarshal in/inout params, and set up to marshal the appropriate +// inout/out/return values later on. +// +void +__stdcall +IIOP_ServerRequest::params ( + CORBA_NVList_ptr list, + CORBA_Environment &env +) +{ + unsigned i; + + env.clear (); + + // + // Save params for later use when marshaling reply + // + _params = list; + + // + // Then unmarshal each "in" and "inout" parameter + // + for (i = 0; i < list->count (); i++) { + CORBA_NamedValue_ptr nv; + CORBA_Any_ptr any; + CORBA_TypeCode_ptr tc; + void *value; + + nv = list->item (i); + if (!(nv->flags () & (CORBA_ARG_IN | CORBA_ARG_INOUT))) + continue; + + // + // First, make sure the memory into which we'll be unmarshaling + // exists, and is the right size. + // + // NOTE: desirable to have a way to let the dynamic implementation + // routine preallocate this data, for environments where DSI is + // just being used in lieu of a language mapped server side API + // and the size is really knowable in advance. + // + any = nv->value (); + tc = any->type (); + tc->AddRef (); + value = new char [tc->size (env)]; + any->replace (tc, value, CORBA_B_TRUE, env); + + // + // Decrement the refcount of "tc". + // + // The earlier AddRef is needed since Any::replace() releases + // the typecode inside the Any. Without the dup, the reference + // count can go to zero, and the typecode would then be deleted. + // + // This Release ensures that the reference count is correct so + // the typecode can be deleted some other time. + // + tc->Release (); + + // + // Then just unmarshal the value. + // + (void) CDR::decoder (tc, value, 0, _incoming, env); + } + + // + // If any data is left over, it'd be context values ... else error. + // We don't support context values, so it's always an error. + // + if (_incoming->bytes_remaining () != 0) { + dmsg1 ("params(), %d bytes remaining (error)", + _incoming->bytes_remaining ()); + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + } +} + + +// +// Store the result value. There's either an exception, or a result, +// but not both of them. Results (and exceptions) can be reported +// only after the parameter list has been provided (maybe empty). +// +void +__stdcall +IIOP_ServerRequest::result ( + CORBA_Any_ptr value, + CORBA_Environment &env +) +{ + env.clear (); + + if (!_params || _retval || _exception) + env.exception (new CORBA_BAD_INV_ORDER (COMPLETED_NO)); + else + _retval = value; + + // XXX send the message now! +} + + +// +// Store the exception value. +// +void +__stdcall +IIOP_ServerRequest::exception ( + CORBA_ExceptionType type, + CORBA_Any_ptr value, + CORBA_Environment &env +) +{ + if (!_params || _retval || _exception) + env.exception (new CORBA_BAD_INV_ORDER (COMPLETED_NO)); + else { + env.clear (); + _exception = value; + _ex_type = type; + } + + // XXX send the message now! +} + + +// +// Invocation attributes. +// +CORBA_String +__stdcall +IIOP_ServerRequest::op_name () +{ + return _opname; +} + +CORBA_Object_ptr +__stdcall +IIOP_ServerRequest::target () +{ + // XXX implement me!! Code from TCP_OA exists ... + return 0; +} + +CORBA_Principal_ptr +__stdcall +IIOP_ServerRequest::caller () +{ + // XXX ... return client's principal + return 0; +} + +CORBA_ORB_ptr +__stdcall +IIOP_ServerRequest::orb () +{ + return _orb; +} + +TOA_ptr +__stdcall +IIOP_ServerRequest::oa () +{ + return _toa; +} + diff --git a/TAO/IIOP/lib/bridge/svrrqst.hh b/TAO/IIOP/lib/bridge/svrrqst.hh new file mode 100644 index 00000000000..f969971bfb1 --- /dev/null +++ b/TAO/IIOP/lib/bridge/svrrqst.hh @@ -0,0 +1,96 @@ +// @(#)svrrqst.hh 1.3 95/11/04 +// Copyright 1994-1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// Header file for Win32 C/C++/COM interface to CORBA's Dynamic +// Server Skeleton Interface's "Server Request" type. +// + +#ifndef _svrrqst_hh +#define _svrrqst_hh + +#include "runtime/cdr.hh" + +extern const IID IID_IIOP_ServerRequest; + +class _EXPCLASS IIOP_ServerRequest : public CORBA_ServerRequest +{ + public: + // + // Constructor, destructor + // + IIOP_ServerRequest ( + CDR *msg, + CORBA_ORB_ptr the_orb, + TOA_ptr the_toa + ) + : _incoming (msg), _params (0), _retval (0), + _exception (0), + _ex_type (NO_EXCEPTION), + _refcount (1), + _orb (the_orb), + _toa (the_toa) + { } + + virtual ~IIOP_ServerRequest (); + + // + // General ServerRequest operations + // + void __stdcall params ( + CORBA_NVList_ptr list, + CORBA_Environment &env + ); + + void __stdcall result ( + CORBA_Any_ptr value, + CORBA_Environment &env + ); + + void __stdcall exception ( + CORBA_ExceptionType type, + CORBA_Any_ptr value, + CORBA_Environment &env + ); + + // + // Request attributes + // + CORBA_String __stdcall op_name (); + CORBA_Principal_ptr __stdcall caller (); + CORBA_Object_ptr __stdcall target (); + CORBA_ORB_ptr __stdcall orb (); + TOA_ptr __stdcall oa (); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + // private: + CORBA_String _opname; + CDR *_incoming; + CORBA_NVList_ptr _params; + CORBA_Any_ptr _retval; + CORBA_Any_ptr _exception; + CORBA_ExceptionType _ex_type; + + // + // Just drop the refcount, don't destroy the object; most + // of these are stack-allocated. + // + void release () { _refcount--; } + + private: + unsigned _refcount; + CORBA_ORB_ptr _orb; + TOA_ptr _toa; +}; + +#endif // _svrrqst_hh + diff --git a/TAO/IIOP/lib/bridge/tcpoa.cpp b/TAO/IIOP/lib/bridge/tcpoa.cpp new file mode 100644 index 00000000000..5e98c21e82c --- /dev/null +++ b/TAO/IIOP/lib/bridge/tcpoa.cpp @@ -0,0 +1,1026 @@ +// @(#)tcpoa.cpp 1.16 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// IIOP: Small "TCP OA", thinly layered atop IIOP. +// +// Like all OAs, this provides an object creation facility and a simple +// DSI-based operation dispatch facility. However, it's so simple it +// doesn't do any more than that! In particular, it maintains no per-object +// state either on disk or in memory. +// +// NOTE: It's undesirable in firewall and threaded environments to continue +// with the current restriction of only one OA per process. +// + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#if unix +#include <unistd.h> +#include <netdb.h> + +#else +#include <winsock.h> + +#endif + +#include <corba/orb.hh> +#include <corba/toa.hh> + +#include "runtime/thread.hh" +#include "runtime/cdr.hh" +#include "runtime/debug.hh" + +#include "bridge/connmgr.hh" +#include "bridge/tcpoa.hh" +#include "bridge/giop.hh" +#include "bridge/svrrqst.hh" + +#include <initguid.h> + + +#ifdef NO_HOSTNAMES +#include <netinet/in.h> +#include <arpa/inet.h> +#endif // NO_HOSTNAMES + + +#if !defined (DECLARED_GETHOSTNAME) +extern "C" int gethostname (char *, int); +#endif + + + +static TCP_OA *the_oa; +static short tcp_port; +static char namebuf [65]; + + +#ifdef _POSIX_THREADS +static pthread_key_t request_key; +static pthread_mutex_t tcpoa_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t tcpoa_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_attr_t thread_attr; +#endif // _POSIX_THREADS + + +TCP_OA::TCP_OA ( + CORBA_ORB_ptr owning_orb, + CORBA_UShort _port, + CORBA_Environment &env +) +{ + assert (the_oa == 0); + + port = _port; + do_exit = CORBA_B_FALSE; + _orb = owning_orb; + call_count = 0; + skeleton = 0; + + if (gethostname (namebuf, sizeof namebuf) < 0) { + dsockerr ("gethostname"); + return; + } + +#ifdef NO_HOSTNAMES + // + // In some highly static environments, or where even the most basic + // Internet services are unavailable, it's desirable to use IP addresses + // rather than host names in object references. IP addresses are + // normally bad to use since they need to change. + // + // In general, TCP OA initialization options are needed to control at + // least three kinds of host identifiers to embed in objrefs: + // + // (a) unqualified host names, which is what gethostname() returns + // at most sites; + // + // (b) qualified host names (including fully qualified ones, also + // ones with just subdomain qualification) which are hard to + // discover in a portable way; + // + // (c) "Dot notation" IP addresses, which can get stale after a short + // while ... hosts move between networks, network numbers change, + // and so on. These will also cause problems in the upcoming + // evolution of the Internet to IPv6. + // + // At this time, no general framework is available to control the choice + // of these kinds of identifiers. A remotely administerable framework + // (e.g. Win32 registry) seems like it'd be most appropriate. + // + // NOTE: gethostbyname() is MT-unsafe, call only in one thread!! + // + hostent *hp; + char *dot_notation; + + hp = gethostbyname (namebuf); + assert (hp != 0); + + dot_notation = inet_ntoa (*(in_addr *)hp->h_addr); + assert (dot_notation != 0); + + (void) strcpy (namebuf, dot_notation); +#endif // NO_HOSTNAMES + + // + // Initialize the endpoint ... or try! + // + // NOTE that this sets "port" if it was originally zero. An + // original value of zero indicates that the OS should select + // a port on which this OA will listen. + // + endpoint = server_endpoint::initialize (port, env); + tcp_port = port; + + if (env.exception () != 0) { + dmsg2 ("TCP OA: '%s', port %u", namebuf, port); + the_oa = this; + } +} + +TCP_OA::~TCP_OA () +{ + if (endpoint) { + endpoint->shutdown_connections (GIOP::close_connection, 0); + endpoint = 0; + } +} + + +// +// Public initialisation routine for this OA. +// +TCP_OA_ptr +TCP_OA::init ( + CORBA_ORB_ptr parent, + char *oa_name, + CORBA_Environment &env +) +{ + env.clear (); + +#ifdef _POSIX_THREADS + Critical region (&tcpoa_mutex); +#endif // _POSIX_THREADS + + if (the_oa) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + +#ifdef _POSIX_THREADS + + // + // Initialize POSIX thread stuff: TSD key for request data, and + // attributes for threads that can be dynamically created. + // + // XXX this stuff should be guarded by "pthread_once", it's not + // at all OA-specific. + // + (void) pthread_key_create (&request_key, 0); + + (void) pthread_attr_init (&thread_attr); + (void) pthread_attr_setdetachstate (&thread_attr, PTHREAD_CREATE_DETACHED); + +#ifdef _POSIX_THREAD_PRIORITY_SCHEDULING + (void) pthread_attr_setscope (&thread_attr, PTHREAD_SCOPE_PROCESS); +#endif // _POSIX_THREAD_PRIORITY_SCHEDULING + +#endif // _POSIX_THREADS + + + // + // The "OA Name" is either the service name (normally listed in the + // /etc/services file) or is the string form of the port number. + // These are both controlled by local administration, though in some + // cases the IETF will register port numbers. If the OA name is + // unspecified (null pointer) or zero, the OS assigns some port + // which is currently unused. + // + // XXX getservent() is MT-unsafe; use getservent_r() where it exists + // on the target platform + // + unsigned short port; + struct servent *sp; + + if (oa_name && (sp = getservbyname (oa_name, "tcp")) != 0) + port = (unsigned short) sp->s_port; + else if (oa_name != 0 && atoi (oa_name) != -1) + port = (unsigned short) atoi (oa_name); + else + port = 0; + + the_oa = new TCP_OA (parent, port, env); + + return the_oa; +} + + +// +// Create an objref +// +CORBA_Object_ptr +__stdcall +TCP_OA::create ( + CORBA_OctetSeq _FAR &key, + CORBA_String _FAR type_id, + CORBA_Environment _FAR &env +) +{ + CORBA_String id; + IIOP_Object *data; + + if (type_id) + id = CORBA_string_copy (type_id); + else + id = 0; + data = new IIOP_Object (id); + + if (data != 0) + env.clear (); + else { + env.exception (new CORBA_NO_MEMORY (COMPLETED_NO)); + return 0; + } + + data->profile.iiop_version.major = IIOP::MY_MAJOR; + data->profile.iiop_version.minor = IIOP::MY_MINOR; + data->profile.host = strdup (namebuf); + data->profile.port = port; + data->profile.object_key.length = key.length; + data->profile.object_key.maximum = key.length; + data->profile.object_key.buffer = new CORBA_Octet [(size_t) key.length]; + + // + // Verify the memory allocations we just did ... + // + if (data->profile.host == 0 || data->profile.object_key.buffer == 0) { + env.exception (new CORBA_NO_MEMORY (COMPLETED_NO)); + data->Release (); + return 0; + } + + memcpy (data->profile.object_key.buffer, key.buffer, (size_t) key.length); + + // + // Return the CORBA_Object_ptr interface to this objref. + // + CORBA_Object_ptr new_obj; + + if (data->QueryInterface (IID_CORBA_Object, (void **)&new_obj) + != NOERROR) { + env.exception (new CORBA_INTERNAL (COMPLETED_NO)); + } + data->Release (); + return new_obj; +} + + +// +// Return the key fed into an object at creation time. +// +CORBA_OctetSeq * +__stdcall +TCP_OA::get_key ( + CORBA_Object_ptr , + CORBA_Environment &env +) +{ + // XXX implement me ! ... must have been created by this OA. + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + return 0; +} + + +#ifdef _POSIX_THREADS +// +// Use the TSD key set up as part of OA initialization to get the +// thread-specific data describing this invocation +// +#define request_tsd \ + ((GIOP::RequestHeader *) pthread_getspecific (request_key)) + +#else + +// +// This emulates "thread specific data" to hold data about the request +// that the thread is processing. It needs to be accessed for a variety +// of purposes in the course of request processing. +// +static GIOP::RequestHeader *request_tsd; +#endif // _POSIX_THREADS + + +// +// Return the objref which is target of the call. This must be freed +// by whoever calls the routine. +// +// XXX should be consistent with other explicit/implicit request data +// and have the ORB free it when the call returns; that'll let the cost +// of the four mallocs (yeech!) be amortized over more work. +// +// XXX there's no way to report exceptions here ... +// +CORBA_Object_ptr +__stdcall +_this () +{ + IIOP_Object *data; + opaque *key = &request_tsd->object_key; + + // + // NOTE: The "TCP_OA" can't return the type ID of this object + // since it has no persistent storage. The expectation is that + // code layered on top of this OA, providing whatever storage is + // needed and defining their own policies for object keys, will be + // built on top of this interface; their implementations of the + // "_this()" routine will return a full objref (and also be able + // to answer questions like whether it _is_a given type). + // + if ((data = new IIOP_Object (0)) == 0) { // null type ID + return 0; + } + + data->profile.iiop_version.major = IIOP::MY_MAJOR; + data->profile.iiop_version.minor = IIOP::MY_MINOR; + data->profile.host = strdup (namebuf); + data->profile.port = tcp_port; + data->profile.object_key.length = key->length; + data->profile.object_key.maximum = key->length; + data->profile.object_key.buffer = new CORBA_Octet [(size_t) key->length]; + memcpy (data->profile.object_key.buffer, key->buffer, (size_t) key->length); + + if (data->profile.host == 0 || data->profile.object_key.buffer == 0) { + data->Release (); + return 0; + } + + // + // Return the CORBA_Object_ptr interface to this objref. + // + CORBA_Object_ptr new_obj; + + (void) data->QueryInterface (IID_CORBA_Object, (void **)&new_obj); + data->Release (); + return new_obj; +} + + +// +// return the target's key +// +// NOTE: as with all "in" parameters to a call, this memory is freed +// by the ORB not by the object implementation. +// +CORBA_OctetSeq * +__stdcall +TCP_OA::get_target_key ( + CORBA_Environment &env +) +{ + env.clear (); + + return &request_tsd->object_key; +} + + +// +// return the caller's principal +// +// NOTE: as with all "in" parameters to a call, this memory is freed +// by the ORB not by the object implementation. +// +CORBA_Principal_ptr +__stdcall +TCP_OA::get_client_principal ( + CORBA_Environment &env +) +{ + env.clear (); + + return request_tsd->requesting_principal; +} + + +// +// Dispatch routine that provides most of the IIOP glue ... constructs +// a dynamic ServerRequest and any reply message that's needed. +// +static void +tcp_oa_dispatcher ( + GIOP::RequestHeader &req, + CDR &request_body, + CDR *reply, + void *context, + CORBA_Environment &env +) +{ + IIOP_ServerRequest svr_req ( + &request_body, + the_oa->orb (), + the_oa + ); + + // + // ServerRequest is what does the unmarshaling, driven by typecodes + // that the DSI user provides. Create the ServerRequest, store away + // information that'll be needed by some methods, and call the dispatch + // routine that the user supplied. Then release the reference so it + // can be safely destroyed sometime later. + // + svr_req._opname = req.operation; + +#ifdef _POSIX_THREADS + (void) pthread_setspecific (request_key, &req); +#else + request_tsd = &req; +#endif // _POSIX_THREADS + + TCP_OA::dispatch_context *helper; + + helper = (TCP_OA::dispatch_context *) context; + helper->skeleton (req.object_key, svr_req, helper->context, env); + + svr_req.release (); + + // + // If reply is null, this was a oneway request ... return! + // + if (reply == 0) + return; + + // + // Otherwise check for correct parameter handling, and reply as + // appropriate. + // + // NOTE: if "env" is set, it takes precedence over exceptions + // reported using the mechanism of the ServerRequest. Only system + // exceptions are reported that way ... + // + // XXX Exception reporting is ambiguous; it can be cleaner than + // this. With both language-mapped and dynamic/explicit reporting + // mechanisms, one of must be tested "first" ... so an exception + // reported using the other mechanism could be "lost". Perhaps only + // the language mapped one should be used for system exceptions. + // + CORBA_TypeCode_ptr tc; + const void *value; + + if (!svr_req._params && env.exception () == 0) { + dmsg ("DSI user error, didn't supply params"); + env.exception (new CORBA_BAD_INV_ORDER (COMPLETED_NO)); + } + + if (env.exception () != 0) { // standard exceptions only + CORBA_Environment env2; + CORBA_Exception *x = env.exception (); + CORBA_TypeCode_ptr except_tc = x->type (); + + reply->put_ulong (GIOP::SYSTEM_EXCEPTION); + (void) CDR::encoder (except_tc, x, 0, reply, env2); + + } else if (svr_req._exception) { // any exception at all + CORBA_Exception *x; + CORBA_TypeCode_ptr except_tc; + + x = (CORBA_Exception *) svr_req._exception->value (); + except_tc = svr_req._exception->type (); + + // + // Finish the GIOP Reply header, then marshal the exception. + // + // XXX x->type() someday ... + // + if (svr_req._ex_type == SYSTEM_EXCEPTION) + reply->put_ulong (GIOP::SYSTEM_EXCEPTION); + else + reply->put_ulong (GIOP::USER_EXCEPTION); + + (void) CDR::encoder (except_tc, x, 0, reply, env); + + } else { // normal reply + // + // First finish the GIOP header ... + // + reply->put_ulong (GIOP::NO_EXCEPTION); + + // + // ... then send any return value ... + // + if (svr_req._retval) { + tc = svr_req._retval->type (); + value = svr_req._retval->value (); + (void) CDR::encoder (tc, value, 0, reply, env); + } + + // + // ... followed by "inout" and "out" parameters, left to right + // + unsigned i; + + for (i = 0; i < svr_req._params->count (); i++) { + CORBA_NamedValue_ptr nv = svr_req._params->item (i); + CORBA_Any_ptr any; + + if (!(nv->flags () & (CORBA_ARG_INOUT|CORBA_ARG_OUT))) + continue; + + any = nv->value (); + tc = any->type (); + value = any->value (); + (void) CDR::encoder (tc, value, 0, reply, env); + } + } +} + + +// +// Helper routine that provides IIOP glue for forwarding requests +// to specific objects from one process to another. +// +static GIOP::LocateStatusType +tcp_oa_forwarder ( + opaque &target_key, + CORBA_Object_ptr &forward_reference, + void *ctx +) +{ + TCP_OA::dispatch_context *helper; + CORBA_Environment env; + + helper = (TCP_OA::dispatch_context *) ctx; + assert (helper->check_forward != 0); + helper->check_forward (target_key, forward_reference, helper->context, env); + + if (env.exception () != 0) + return GIOP::UNKNOWN_OBJECT; + else if (forward_reference == 0) + return GIOP::OBJECT_HERE; + else + return GIOP::OBJECT_FORWARD; +} + + +// +// Generic routine to handle a message. +// +void +TCP_OA::handle_message ( + dispatch_context &ctx, + CORBA_Environment &env +) +{ + GIOP::incoming_message (ctx.endpoint->fd, + ctx.check_forward ? tcp_oa_forwarder : 0, + tcp_oa_dispatcher, &ctx, env); + +#ifdef _POSIX_THREADS + Critical region (&tcpoa_mutex); +#endif // _POSIX_THREADS + + call_count--; +} + + +#ifdef _POSIX_THREADS +// +// For cases where the OA creates new threads to handle messages (which +// are typically, but not always, GIOP::Request messages) we need to have +// a routine for that new thread to execute. This is that routine: its +// role is only to set up to call the common "handle_message" routine, +// knowing that it's not the thread which was initially handed the message. +// +// In "aggressive" threading mode, it continues reading from the connection +// until the connection goes away (e.g. client goes away, or server does +// its shutdown work). This is a big win. +// +// XXX this needs to set up so it goes away with pthread_cancel(). We +// lack any kind of "timed read" primitive so the best we could otherwise +// do is to select (with timeout) before issuing the read, possibly with +// a switch to another thread in between. +// +void * +TCP_OA::worker (void *arg) +{ + CORBA_Environment env; + dispatch_context *context = (dispatch_context *)arg; + + do { + dmsg1 ("worker starting on FD %d", context->endpoint->fd); + context->oa->handle_message (*context, env); + + if (env.exception () != 0) { + dexc (env, "TCP_OA, worker"); + env.clear (); + } + } while (context->aggressive && context->endpoint->fd != -1); + + delete context; + return 0; +} +#endif // _POSIX_THREADS + + +// +// Block till some request comes in, and call "skeleton" when it does. +// Uses helper routines above to do most of the work. +// +// THREADING NOTE: Only one thread may call this at a time, due to some +// underlying issues with select(). If you want a model wherein a finite +// pool of threads handles your requests, this doesn't do that. (This is +// probably a good thing: it's all but impossible to accurately bound the +// number of concurrent requests any given server will need to handle.) +// +void +__stdcall +TCP_OA::get_request ( + TOA::dsi_handler handler, + void check_forward ( + CORBA_OctetSeq &key, + CORBA_Object_ptr &fwd_ref, + void *ctx, + CORBA_Environment &env + ), + CORBA_Boolean do_thr_create, + void *app_state, + timeval *timeout, + CORBA_Environment &env +) +{ + server_endpoint *fd; + + env.clear (); + + // + // Two bits of OA-private state need to be guarded by a critical + // region: the "do_exit" flag, and the "call_count" flag. These + // are used in clean shutdown, and can be modified here. + // + { +#ifdef _POSIX_THREADS + Critical region (&tcpoa_mutex); +#endif // _POSIX_THREADS + + // + // Applications sometimes make mistakes: here, it'd be + // polling for a new request after initiating shutdown. + // + if (do_exit) { + dmsg ("called get_request during OA shutdown"); + env.exception (new CORBA_BAD_INV_ORDER (COMPLETED_NO)); + return; + } + +#ifndef _POSIX_THREADS + // + // Here, some unthreaded app asked for thread support. Ooops! + // + if (do_thr_create) { + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + dexc (env, "TCP_OA, unthreaded: can't create worker threads"); + return; + } +#endif // _POSIX_THREADS + + // + // Get a request/message ... if no file descriptor is returned, + // the application-specified timeout was reached, leading to a + // clean shutdown. Otherwise we flag an incoming message (so + // shutdown will know it can't start yet), and leave the critical + // section. Some other thread could now get a request on some + // other connection, if one's ready. + // + // THREADING NOTE: what block_for_connection() returns will not + // be read or written by any other thread until it's released ... + // that is, until "fd" goes out of scope. At least, in the + // current implementation, which doesn't let threads share a + // connection. + // + // Also, note that the underlying constraint of only allowing a + // single thread to block_for_connection() call bubbles up here too. + // +#ifdef _POSIX_THREADS + static int blocking; // = 0 + + if (blocking) { + dmsg ("concurrent TCP_OA::get_request() calls"); + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + return; + } else + blocking = 1; + + region.leave (); +#endif // _POSIX_THREADS + + // + // Get a connection and hand it to method code. + // + // There are four "threading modes" currently possible with + // this software, though only two are exposed in the OA API. + // + // - Single-Threaded ... where either no threads are + // used, or method code only sees one thread. + // + // - Simple MT ... like single threaded, except that + // (a) method code can see multiple threads, (b) a thread + // context switch happens before incoming methods get + // processed, and (c) there's expensive handshaking re + // connections when they're released. + // + // - "Eager" MT ... like simple MT, except that the context + // switch (b) is gone, and incoming data is handled right + // away by a thread that's always blocked in a read. The + // initial request latency is lower than "Simple MT". + // + // - "Aggressive" MT ... like "eager" MT except that the + // costly handshaking (c) is removed along with thread + // creation costs. Throughput is higher than with any + // of the other models, since it's not just initial + // requests which benefit from reduced latency and + // since overall MT-related costs are minimized. + // + // To the application, only "single threaded" and "MT" policies + // are exposed ... the rest is implementation details, all they + // really care about is whether method code seems multiple threads + // and the quality of the implementation (size, speed, etc). + // + // XXX Right now we equate "MT" to "Aggressive", despite problems. + // If clients behave well, they're not an issue; in real systems, + // we know clients don't always behave. + // + // One forward-looking way is to use pthread_cancel to kill idle + // workers ... that looks forward to a later implementation which + // always has a read outstanding on a connection, and so can also + // handle GIOP::CancelRequest messages. + // + + fd = endpoint->block_for_connection (do_thr_create, timeout, env); + +#ifdef _POSIX_THREADS + region.enter (); + blocking = 0; +#endif // _POSIX_THREADS + + if (env.exception () != 0) { + dexc (env, "TCP_OA, block for connection"); + return; + } + if (fd == 0) { + do_exit = CORBA_B_TRUE; + return; + } + + // + // THREADING NOTE: This is the last of the need to have the OA + // locked ... next, let other threads in to access its state. + // + call_count++; + } + + // + // OK, now we've got a connection with a "live" IIOP message ready. + // + // If we're "eager", there's not actually any data on that connection + // yet ... but we still hand it to some other thread as if it were + // all there. This gets all the setup out of the critical path so that + // when data does arrive, its latency is minimal: straight into the + // IIOP data buffer, in the best (zero copy) case, with no lingering in + // kernel read queues. + // + if (do_thr_create) { +#ifdef _POSIX_THREADS + // + // Want to handle it in another thread. That means handing off a + // bunch of information to that thread and then having it do the + // work ... the simplest alternative is to heap-allocate the data + // and hand it over. That involves no handshaking with the thread + // we're about to create. Note that we're also handing off the + // connection resource too -- when this dispatch context gets + // destroyed, only then is the connection released. + // + dispatch_context *ctx; + + ctx = new dispatch_context; + ctx->skeleton = handler; + ctx->check_forward = check_forward; + ctx->context = app_state; + ctx->oa = this; + ctx->endpoint = fd; + ctx->aggressive = do_thr_create; + + // + // Actually create the thread. + // + int errcode; + pthread_t tid; + + // XXX Devpro bug at "-O3" + void *(*func)(void *) = worker; + + errcode = pthread_create (&tid, &thread_attr, func, ctx); + + if (errcode == 0) + return; + + // + // Can't create a thread as requested. Rather than handling it + // in another thread, we must then handle it ourselves. It's bad + // news to drop requests, as would happen if we were to just + // report an exception. + // + // XXX this should be logged as some kind of system fault; + // an administrator would need to deal with these failures + // if they happen repatedly. + // + dmsg2 ("pthread_create error: %d (%s)", errcode, + strerror (errcode)); + delete context; + +#endif // _POSIX_THREADS + } + + // + // Handle it in this thread. We can do it without any need + // to dynamically allocate memory. + // + dispatch_context ctx; + + ctx.skeleton = handler; + ctx.check_forward = check_forward; + ctx.context = app_state; + ctx.oa = this; + ctx.endpoint = fd; + +#ifdef _POSIX_THREADS + ctx.aggressive = CORBA_B_FALSE; +#endif + + handle_message (ctx, env); + + // + // Don't report any errors from the application/skeleton back to the + // top level ... the application already has a variety of means to + // pass whatever data needs passing, and we don't need to confuse + // things by mixing ORB and application errors here. + // + if (env.exception () != 0) { + dexc (env, "TCP_OA, handle incoming message"); + env.clear (); + } +} + + +// +// Used by method code to ask the OA to shut down. +// +void +__stdcall +TCP_OA::please_shutdown ( + CORBA_Environment &env +) +{ +#ifdef _POSIX_THREADS + Critical region (&tcpoa_mutex); +#endif // _POSIX_THREADS + + env.clear (); + do_exit = CORBA_B_TRUE; +} + +// +// Used by non-method code to tell the OA to shut down. +// +void +__stdcall +TCP_OA::clean_shutdown ( + CORBA_Environment &env +) +{ +#ifdef _POSIX_THREADS + Critical region (&tcpoa_mutex); +#endif // _POSIX_THREADS + + env.clear (); + + if (call_count != 0) { + dmsg ("called clean_shutdown with requests outstanding"); + env.exception (new CORBA_BAD_INV_ORDER (COMPLETED_NO)); + return; + } + + endpoint->shutdown_connections (GIOP::close_connection, 0); + endpoint = 0; +} + + +// +// For TOA -- TOA operations for which we provide the vtable entry +// + +void +__stdcall +TCP_OA::register_dir ( + TOA::dsi_handler handler, + void *ctx, + CORBA_Environment &env +) +{ + if (handler == 0) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return; + } + + skeleton = handler; + context = ctx; + + env.clear (); +} + +void +__stdcall +TCP_OA::get_request ( + CORBA_Boolean use_threads, + struct timeval *tvp, + CORBA_Environment &env +) +{ + // + // API spec calls for the DIR to be registered and for this to + // report an invocation order problem if this is called after + // shutdown was started (app can always avoid the latter). + // + if (skeleton == 0) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return; + } + + // + // Just call the IIOP level dispatch code without allowing it to + // ever forward requests to another TCP_OA. + // + get_request (skeleton, 0, use_threads, context, tvp, env); +} + + +// +// For COM -- IUnknown operations, we provide the vtable entry +// + +// {A201E4C4-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_TCP_OA, +0xa201e4c4, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +TCP_OA::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&tcpoa_lock); +#endif + + return ++refcount; +} + +ULONG +__stdcall +TCP_OA::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&tcpoa_lock); +#endif + + if (--refcount != 0) + return refcount; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + delete this; + return 0; +} + +HRESULT +__stdcall +TCP_OA::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_TCP_OA == riid + || IID_TOA == riid + || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} diff --git a/TAO/IIOP/lib/bridge/tcpoa.hh b/TAO/IIOP/lib/bridge/tcpoa.hh new file mode 100644 index 00000000000..47212d4f22b --- /dev/null +++ b/TAO/IIOP/lib/bridge/tcpoa.hh @@ -0,0 +1,276 @@ +// @(#)tcpoa.hh 1.6 95/10/02 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Simple "TCP" OA ... lets one create objrefs very thinly layered atop +// TCP addressing. This OA holds no state whatsoever (!!!!), and only +// supports transient objects. +// +// You may well want to build more "advanced" OAs on top of this one, +// providing features such as activation of persistent objects. (The +// BOA, for example, provides a persistent object activation facility.) +// +// In particular: you will probably want to build atop this to plug in +// skeletons for any language binding, mapping individual objrefs to type +// and implementation information so support for CORBA::Object operations +// such as get_interface(), is_a(), get_implementation(), and the test for +// non-existence can all be handled by an Object Adapter, not by any +// application code. +// +// This might also be the appropriate level to define interfaces that'd +// let MT-unsafe window system toolkits hardwire their event dispatch +// frameworks into the one used by this ORB. For MT-safe window system +// toolkits, such integration is not needed because everything works +// fine if the application just allocates one thread to handle dispatch +// of each subsystem's events (e.g. ORB, GUI, etc). +// + +#ifndef _TCPOA_HH +#define _TCPOA_HH + +class _EXPCLASS TCP_OA; +typedef TCP_OA *TCP_OA_ptr; + +extern const IID IID_TCP_OA; + +class _EXPCLASS TCP_OA : public TOA { + public: + //////////////////////////////////////////////////////////////////////// + // + // TOA support ... TOA is intended to be a public API that anyone can + // use, including skeletons. + // + //////////////////////////////////////////////////////////////////////// + + CORBA_Object_ptr __stdcall create ( + CORBA_OctetSeq &obj_id, + CORBA_String type_id, + CORBA_Environment &env + ); + + void __stdcall register_dir ( + TOA::dsi_handler handler, + void *context, + CORBA_Environment &env + ); + + void __stdcall get_request ( + CORBA_Boolean use_threads, + struct timeval *tvp, + CORBA_Environment &env + ); + + void __stdcall please_shutdown ( + CORBA_Environment &env + ); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + + //////////////////////////////////////////////////////////////////////// + // + // TCP_OA SPECIFIC from here on down ... all of this is subject to + // change and simplification. It's intended to be an internal API + // providing efficient access to all basic IIOP functionality. + // + //////////////////////////////////////////////////////////////////////// + + // + // OA initialisation, per the template in OMG TC doc 94-9-46 + // + // NOTE that since this OA is not defined by OMG, it doesn't + // go into the CORBA (and hence ORB) scope and acquiring this + // OA is not an operation on any particular ORB. + // + // Also, this needs no configuration beyond which OA is used; + // there's no need for argc/argv to control any initialization + // options. + // + static TCP_OA_ptr init ( + CORBA_ORB_ptr which_orb, + char *oa_name, + CORBA_Environment &env + ); + + // + // Block till some request comes in, or "timeout" passes. Handle any + // requests with "handle_request", passing it "context" for access to + // non-global application state. + // + // If the "do_thr_create" flag is set, "handle_request" is invoked in + // a new thread rather than by the calling thread. + // + // If "check_forward" is null, all incoming requests are passed to + // handle_request() and requests for objects can't be forwarded so + // they arrive at any other process. + // + // If "check_forward" is non-null, the function is called when the OA + // needs to establish whether a request for a particular object (as + // identified by "key") are handled in this process. If it returns an + // exception, the object is deemed not to exist (this is authoritative). + // If the "fwd_ref" returned is non-null, the request is forwarded to + // that object reference. Otherwise (no exception, null "fwd_ref") + // requests for that object may arrive via handle_request(). + // + // This routine returns after a request arrives, regardless of whether + // a thread started processing. + // + void __stdcall get_request ( + TOA::dsi_handler handle_request, + void check_forward ( + CORBA_OctetSeq &key, + CORBA_Object_ptr &fwd_ref, + void *context, + CORBA_Environment &env + ), + CORBA_Boolean do_thr_create, + void *context, + timeval *timeout, + CORBA_Environment &env + ); + + // + // OA user asks for a clean shutdown of the OA after currently + // active calls complete. OA "requester" (calls get_request) + // asks if we're shutting down, and if so closes down transport + // cleanly. + // + CORBA_Boolean shutting_down () + { return do_exit; } + + void clean_shutdown ( + CORBA_Environment &env + ); + + // + // When dispatching a request to an object, you need to be able to get + // the object key you used to create the reference. It's the main way + // servers distinguish two object references from each other. + // + CORBA_OctetSeq *__stdcall get_key ( + CORBA_Object_ptr obj, + CORBA_Environment &env + ); + + // + // OA-specific state + // + CORBA_ORB_ptr orb () const { return _orb; } + + // + // Various request-specific state. + // + CORBA_OctetSeq *__stdcall get_target_key ( + CORBA_Environment &env + ); + CORBA_Principal_ptr __stdcall get_client_principal ( + CORBA_Environment &env + ); + + // PRIVATE: + + // + // Data structure passed as "context" to the GIOP code, which then + // calls back one of the two helper routines as part of handling any + // particular incoming request. + // + struct dispatch_context { + TOA::dsi_handler skeleton; + void (*check_forward) ( + CORBA_OctetSeq &key, + CORBA_Object_ptr &fwd_ref, + void *context, + CORBA_Environment &env + ); + void *context; + TCP_OA *oa; + autorelease <server_endpoint> + endpoint; +#ifdef _POSIX_THREADS + CORBA_Boolean aggressive; +#endif // _POSIX_THREADS + }; + + private: + CORBA_UShort port; + CORBA_Boolean do_exit; + CORBA_ORB_ptr _orb; + unsigned call_count; + unsigned refcount; + server_endpoint *endpoint; + + TOA::dsi_handler skeleton; + void *context; + + // + // Used internally by threaded (and unthreaded) code to + // dispatch incoming GIOP messages + // + void handle_message ( + dispatch_context &context, + CORBA_Environment &env + ); + +#ifdef _POSIX_THREADS + // + // Used internally by threaded code to process incoming messages. + // + static void *worker (void *arg); +#endif // _POSIX_THREADS + + // + // Constructor -- build it with a port number to listen to + // + TCP_OA ( + CORBA_ORB_ptr orb_arg, + CORBA_UShort port, + CORBA_Environment &env + ); + virtual ~TCP_OA (); + + // + // Copy and assignment: just say no + // + TCP_OA (const TCP_OA &src); + TCP_OA &operator = (const TCP_OA &src); + +#if defined (__GNUG__) + // + // G++ (even 2.6.3) stupidly thinks instances can't be + // created. This de-warns. + // + friend class everyone_needs_a_friend; +#endif +}; + +typedef TCP_OA *TCP_OA_ptr; + + +// +// Method code (only!) is defined to have access to three functions, for +// the orb, object, and OA involved in a call. +// +// In this version we implement these by access through routines that +// use thread-specific data in multithreaded environments; the routines +// themselves (implementation of the symbol) are not public, but the +// symbol is usable in any method code. +// +// NOTE: it'd be preferable to define the "orb" and "tcp_oa" symbols +// only within method code (e.g. protected member functions on some +// servant base class), but as we don't yet implement much server +// side mapping that's impractical. +// +// XXX many of these are now methods on CORBA_ServerRequest... +// +extern CORBA_Object_ptr __stdcall _this (void); + +#endif // _TCPOA_HH + diff --git a/TAO/IIOP/lib/corba/any.hh b/TAO/IIOP/lib/corba/any.hh new file mode 100644 index 00000000000..4b0a6590e5b --- /dev/null +++ b/TAO/IIOP/lib/corba/any.hh @@ -0,0 +1,87 @@ +// +// Header file for Win32 C/C++/COM interface to CORBA's "Any" type. +// +// Class "Any" can wrap values of any type, with the assistance +// of a TypeCode to describe that type. +// +// XXX should find a way to make its memory allocation always go +// within the appropriate OLE heap... +// +typedef CORBA_Any *CORBA_Any_ptr; + +extern const IID IID_CORBA_Any; + +class CORBA_Any : public IUnknown +{ + public: + // minor codes for exceptional returns + enum { + uninitialised_type = 0xf000, + value_without_type, + unsupported_operation + }; + + CORBA_Any (); + CORBA_Any ( + CORBA_TypeCode_ptr type, + void *value = 0, + CORBA_Boolean orb_owns_data + = CORBA_B_FALSE + ); + CORBA_Any (const CORBA_Any &a); + virtual ~CORBA_Any (); + + void *operator new (size_t, const void *p) + { return (void *) p; } + void *operator new (size_t s) + { return ::operator new (s); } + void operator delete (void *p) + { ::operator delete (p); } + + // + // NOTE: 94-9-14 has assignment operator plus many insertion, + // extraction operators, various from_xx and to_xx helper classes. + // + + void replace ( + CORBA_TypeCode_ptr type, + const void *value, + CORBA_Boolean orb_owns_data, + CORBA_Environment &env + ); + + CORBA_TypeCode_ptr type () const; + void *value () const; + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + // + // Conversion to/from COM Variant types: copy constructor, + // assignment operator, cast. + // + CORBA_Any (const VARIANT &src); + CORBA_Any &operator = (const VARIANT &src); + operator VARIANT (); + + private: + CORBA_TypeCode_ptr _type; + void *_value; + CORBA_Boolean _orb_owns_data; + + unsigned _refcnt; + + // NOT PROVIDED + CORBA_Any &operator = (const CORBA_Any &a); + + // + // 94-9-14 hides unsigned char insert/extract + // +}; diff --git a/TAO/IIOP/lib/corba/corbacom.hh b/TAO/IIOP/lib/corba/corbacom.hh new file mode 100644 index 00000000000..2a8ea5e23c2 --- /dev/null +++ b/TAO/IIOP/lib/corba/corbacom.hh @@ -0,0 +1,108 @@ +// @(#)corbacom.hh 1.2 95/10/02 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// CORBA C/C++/COM mapping for Win32 +// + +#include <objbase.h> // Win32 name for "compobj.h" + +#if SIZEOF_BOOL != 0 +typedef bool CORBA_Boolean; +#define CORBA_B_FALSE false +#define CORBA_B_TRUE true + +#else // "bool" not builtin to this compiler +typedef int CORBA_Boolean; +enum { CORBA_B_FALSE = 0, CORBA_B_TRUE = 1 }; +#endif // "bool" not builtin + +typedef unsigned char CORBA_Octet; + +typedef short CORBA_Short; +typedef unsigned short CORBA_UShort; + +// +// CORBA "Long" (and its unsigned cousin) are 32 bits, just like +// on almost all C/C++ compilers. +// +#if SIZEOF_LONG == 4 +typedef long CORBA_Long; +typedef unsigned long CORBA_ULong; +#else +// just assume "int" is 4 bytes long ... +typedef int CORBA_Long; +typedef unsigned CORBA_ULong; +#endif // SIZEOF_LONG != 4 + +// +// 94-9-32 Appendix A, also the OMG C++ mapping, stipulate that +// 64 bit integers are "LongLong". +// +// NOTE: those are IDL extensions, not yet standard. +// +#if SIZEOF_LONG_LONG == 8 +typedef long long CORBA_LongLong; +typedef unsigned long long CORBA_ULongLong; +#elif SIZEOF_LONG == 8 +typedef long CORBA_LongLong; +typedef unsigned long CORBA_ULongLong; +#elif defined (_MSC_VER) && _MSC_VER >= 900 +typedef __int64 CORBA_LongLong; +typedef unsigned __int64 CORBA_ULongLong; +#else +// +// If "long long" isn't native, programs can't use these +// data types in normal arithmetic expressions. If any +// particular application can cope with the loss of range +// it can define conversion operators itself. +// +# define NONNATIVE_LONGLONG +#if defined (WORDS_BIGENDIAN) +struct CORBA_LongLong { CORBA_Long h, l; }; +struct CORBA_ULongLong { UCORBA_Long h, l; }; +#else +struct CORBA_LongLong { CORBA_Long l, h; }; +struct CORBA_ULongLong { CORBA_ULong l, h; }; +#endif // !WORDS_BIGENDIAN +#endif // no native 64 bit integer type + +typedef float CORBA_Float; +typedef double CORBA_Double; + +// +// 94-9-32 Appendix A defines a 128 bit floating point "long +// double" data type, with greatly extended precision and +// four more bits of exponent (compared to "double"). +// +// NOTE: that is an IDL extension, not yet standard. +// +#if SIZEOF_LONG_DOUBLE == 16 +typedef long double CORBA_LongDouble; +#else +#define NONNATIVE_LONGDOUBLE +struct CORBA_LongDouble { + char ld [16]; +}; +#endif // SIZEOF_LONG_DOUBLE != 16 + +typedef char CORBA_Char; +typedef CORBA_Char *CORBA_String; + +CORBA_String CORBA_string_alloc (CORBA_ULong len); +CORBA_String CORBA_string_copy (const CORBA_Char *const); +void CORBA_string_free (CORBA_Char *const); + +// +// 94-9-32 Appendix A defines 16-bit UNICODE characters as +// "WChar", and null-terminated strings of them as "WString". +// +// NOTE: those are IDL extensions, not yet standard. +// +typedef wchar_t CORBA_WChar; +typedef CORBA_WChar *CORBA_WString; + +CORBA_WString CORBA_wstring_alloc (CORBA_ULong len); +CORBA_WString CORBA_wstring_copy (const CORBA_WChar *const); +void CORBA_wstring_free (CORBA_WChar *const); + diff --git a/TAO/IIOP/lib/corba/except.hh b/TAO/IIOP/lib/corba/except.hh new file mode 100644 index 00000000000..0dacdfb0900 --- /dev/null +++ b/TAO/IIOP/lib/corba/except.hh @@ -0,0 +1,186 @@ +// +// Header file for Win32 C/C++/COM interface to a CORBA ORB. +// +// This file defines way in which CORBA exceptions are reported. +// + +// +// CORBA2-specified exception hierarchy. +// +// All exceptions have a type (represented by a TypeCode) and a widely +// scoped type ID (in the TypeCode) that generated by any OMG-IDL compiler +// and available through the Interface Repositories. Think of it as a +// "globally scoped" name distinguishing each exception. +// +extern const IID IID_CORBA_Exception; +extern const IID IID_CORBA_UserException; +extern const IID IID_CORBA_SystemException; + +class _EXPCLASS CORBA_Exception : public IUnknown { + public: + CORBA_Exception (const CORBA_Exception &src); + CORBA_Exception &operator = (const CORBA_Exception &src); + + void *operator new (size_t, const void *p) + { return (void *) p; } + void *operator new (size_t s) + { return ::operator new (s); } + void operator delete (void *p) + { ::operator delete (p); } + + + const CORBA_String id () const; + const CORBA_TypeCode_ptr type () const; + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + CORBA_Exception ( + CORBA_TypeCode_ptr type + ); + virtual ~CORBA_Exception (); + private: + CORBA_TypeCode_ptr _type; + unsigned _refcnt; +}; +typedef CORBA_Exception *CORBA_Exception_ptr; + +// +// User exceptions are those defined by application developers +// using OMG-IDL. +// +class _EXPCLASS CORBA_UserException : public CORBA_Exception { + public: + CORBA_UserException (CORBA_TypeCode_ptr tc); + ~CORBA_UserException (); + protected: + // copy and assignment operators +}; + +// +// System exceptions are those defined in the CORBA spec; OMG-IDL +// defines these. +// +enum CORBA_CompletionStatus { + COMPLETED_YES, // successful or exceptional completion + COMPLETED_NO, // didn't change any state; retry is OK + COMPLETED_MAYBE // can't say what happened; retry unsafe +}; + +class _EXPCLASS CORBA_SystemException : public CORBA_Exception { + public: + // 94-9-14 also sez: public copy constructor + // and assignment operator. + + CORBA_SystemException ( + CORBA_TypeCode_ptr tc, + CORBA_ULong code, + CORBA_CompletionStatus completed + ); + ~CORBA_SystemException (); + + CORBA_ULong minor () const { return _minor; } + void minor (CORBA_ULong m) { _minor = m; } + + CORBA_CompletionStatus completion () const { return _completed; } + void completion (CORBA_CompletionStatus c) + { _completed = c; } + + private: + CORBA_ULong _minor; + CORBA_CompletionStatus _completed; +}; + + +// +// Declarations for all of the CORBA standard exceptions. +// +// XXX shouldn't have a default minor code, at least for code that's +// inside the ORB. All minor codes should be symbolically catalogued. +// +#define SYSEX(name) \ +extern CORBA_TypeCode_ptr _tc_CORBA_ ## name ; \ +class _EXPCLASS CORBA_ ## name : public CORBA_SystemException { \ + public: \ + CORBA_ ## name ( \ + CORBA_CompletionStatus completed, \ + CORBA_ULong code = 0xffff0000L \ + ) : CORBA_SystemException ( \ + _tc_CORBA_ ## name, \ + code, completed) { } } + +SYSEX (UNKNOWN); +SYSEX (BAD_PARAM); +SYSEX (NO_MEMORY); +SYSEX (IMP_LIMIT); +SYSEX (COMM_FAILURE); +SYSEX (INV_OBJREF); +SYSEX (OBJECT_NOT_EXIST); +SYSEX (NO_PERMISSION); +SYSEX (INTERNAL); +SYSEX (MARSHAL); +SYSEX (INITIALIZE); +SYSEX (NO_IMPLEMENT); +SYSEX (BAD_TYPECODE); +SYSEX (BAD_OPERATION); +SYSEX (NO_RESOURCES); +SYSEX (NO_RESPONSE); +SYSEX (PERSIST_STORE); +SYSEX (BAD_INV_ORDER); +SYSEX (TRANSIENT); +SYSEX (FREE_MEM); +SYSEX (INV_IDENT); +SYSEX (INV_FLAG); +SYSEX (INTF_REPOS); +SYSEX (BAD_CONTEXT); +SYSEX (OBJ_ADAPTER); +SYSEX (DATA_CONVERSION); + +#undef SYSEX + +// +// A CORBA_Environment is a way to automagically ensure that exception +// data is freed -- the "var" class for Exceptions. It adds just a bit +// of convenience function support, helping classify exceptions as well +// as reducing memory leakage. +// +enum CORBA_ExceptionType { + NO_EXCEPTION, + SYSTEM_EXCEPTION, + USER_EXCEPTION +}; + +class CORBA_Environment { + public: + CORBA_Environment () : _exception (0) { } + ~CORBA_Environment () { clear (); } + + CORBA_Exception_ptr exception () const { return _exception; } + void exception (CORBA_Exception *ex) + { clear (); _exception = ex; } + + CORBA_ExceptionType exception_type () const; + const CORBA_String exception_id () const; + + void clear () + { + if (_exception) { + _exception->Release (); + _exception = 0; // XXX + } + } + + private: + CORBA_Exception_ptr _exception; + + // these are not provided + CORBA_Environment (const CORBA_Environment &src); + CORBA_Environment &operator = (const CORBA_Environment &src); +}; diff --git a/TAO/IIOP/lib/corba/nvlist.hh b/TAO/IIOP/lib/corba/nvlist.hh new file mode 100644 index 00000000000..e7b8b8e20dd --- /dev/null +++ b/TAO/IIOP/lib/corba/nvlist.hh @@ -0,0 +1,119 @@ +// @(#)nvlist.hh 1.5 95/09/12 +// Copyright 1995 by Sun Microsystems, Inc. +// +// NamedValue ... these occur only in "NVList" (named value list) +// data structures. The binary form of the data structure is frozen +// and visible to programs using it (e.g. from C). The C++ class +// supports some programming discipline, e.g. to avoid memory leaks. +// +// They just represent parameters to calls. The name is optional, +// and the value is packaged as an Any. The flags indicate parameter +// mode, and some ownership rules for "top level" memory. +// +class _EXPCLASS CORBA_NamedValue; + +void CORBA_release (CORBA_NamedValue_ptr x); +CORBA_Boolean CORBA_is_nil (CORBA_NamedValue_ptr x); + +// No IID required +enum { + CORBA_ARG_IN = 0x01, + CORBA_ARG_OUT = 0x02, + CORBA_ARG_INOUT = 0x04, + CORBA_IN_COPY_VALUE = 0x08, + CORBA_OUT_LIST_MEMORY = 0x10 +}; + +class _EXPCLASS CORBA_NamedValue +{ + public: + const CORBA_String _FAR name () { return (const CORBA_String) _name; } + CORBA_Any_ptr _FAR value () { return &_any; } + CORBA_Flags flags () const { return _flags; } + + ~CORBA_NamedValue (); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + private: + unsigned _refcount; + + CORBA_Any _any; + CORBA_Flags _flags; + const CORBA_Char *_FAR _name; + + CORBA_NamedValue () : _flags (0), _name (0) { } + + friend class CORBA_NVList; + friend class CORBA_Request; +}; + + +// +// NVList ... this is used in the (client side) DII (Dynamic Invocation +// Interface) to hold parameters, except for the return parameter. It's +// used in the same role in the (server side) DSI (Dynamic Skeleton +// Interface). +// +// Each user (client, server) provides the typecode and memory for each +// parameter using an NVList, then talks to the ORB using a Request or +// ServerRequest pseudo-object. The ORB copies data to/from the IPC +// messages (e.g. IIOP::Request, IIOP::Response) as appropriate. +// +class _EXPCLASS CORBA_NVList; + +void CORBA_release (CORBA_NVList_ptr x); +CORBA_Boolean CORBA_is_nil (CORBA_NVList_ptr x); + +extern "C" const IID IID_CORBA_NVList; + +class _EXPCLASS CORBA_NVList +{ + public: + CORBA_ULong count () const + { return _len; } + + CORBA_NamedValue_ptr add_value ( + const CORBA_Char *_FAR , + const CORBA_Any _FAR &, + CORBA_Flags, + CORBA_Environment _FAR & + ); + + CORBA_NamedValue_ptr item (CORBA_Long n) const + { return &_values [(unsigned) n]; } + + ~CORBA_NVList (); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + private: + CORBA_NamedValue *_FAR _values; + unsigned _max; + unsigned _len; + unsigned _refcount; + + CORBA_NVList () + : _values (0), _max (0), + _len (0), _refcount (1) + { } + + friend class CORBA_ORB; + friend class CORBA_Request; +}; diff --git a/TAO/IIOP/lib/corba/object.hh b/TAO/IIOP/lib/corba/object.hh new file mode 100644 index 00000000000..6296fa7682a --- /dev/null +++ b/TAO/IIOP/lib/corba/object.hh @@ -0,0 +1,111 @@ +// @(#)object.hh 1.7 95/11/30 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// Header file for Win32 interface to CORBA's base "Object" type. +// +// A "Object" is an entity that can be the target of an invocation +// using an ORB. All CORBA objects provide this functionality. +// See the CORBA 2.0 specification for details. +// +typedef class CORBA_Object *CORBA_Object_ptr; +void CORBA_release (CORBA_Object_ptr obj); +CORBA_Boolean CORBA_is_nil (CORBA_Object_ptr obj); + +extern const IID IID_CORBA_Object; + +class _EXPCLASS CORBA_Object : public IUnknown +{ + public: + static CORBA_Object_ptr _duplicate (CORBA_Object_ptr obj); + static CORBA_Object_ptr _nil (); + + // + // These calls correspond to over-the-wire operations, or at + // least do so in many common cases. The normal implementation + // assumes a particular simple, efficient, protocol-neutral + // interface for making such calls, but may be overridden when + // it appears appropriate. + // + virtual CORBA_InterfaceDef_ptr __stdcall + _get_interface ( + CORBA_Environment &env + ); + virtual CORBA_Boolean __stdcall + _is_a ( + const CORBA_Char *logical_type_id, + CORBA_Environment &env + ); + virtual CORBA_ImplementationDef_ptr __stdcall + _get_implementation ( + CORBA_Environment &env + ); + virtual CORBA_Boolean __stdcall + _non_existent ( + CORBA_Environment &env + ); + + // + // The mapping for create_request is split into two forms, corresponding to + // the two usage styles described in CORBA section 6.2.1. + // + // The default implementation of this method uses the same simple, + // multi-protocol remote invocation interface as is assumed by the + // calls above ... that's how it can have a default implementation. + // + virtual void __stdcall + _create_request( + const CORBA_Char *operation, + CORBA_NVList_ptr arg_list, + CORBA_NamedValue_ptr result, + CORBA_Request_ptr &request, + CORBA_Flags req_flags, + CORBA_Environment &env + ); + virtual CORBA_Request_ptr __stdcall + _request ( + const CORBA_Char *operation, + CORBA_Environment &env + ); + + // + // These two rely on the representation of the object reference's + // private state. Since that changes easily (when different + // ORB protocols are in use) there is no default implementation. + // + virtual CORBA_ULong __stdcall + _hash ( + CORBA_ULong maximum, + CORBA_Environment &env + ); + virtual CORBA_Boolean __stdcall + _is_equivalent ( + CORBA_Object_ptr other_obj, + CORBA_Environment &env + ); + // + // Stuff required for COM IUnknown support ... this class is + // intended to aggregate or be contained with others, which + // in combination provide all requisite CORBA/COM support. + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + CORBA_Object (IUnknown *p); + virtual ~CORBA_Object (); + + private: + IUnknown *const parent; + + // + // these two are not provided! + // + CORBA_Object (const CORBA_Object &); + CORBA_Object &operator = (const CORBA_Object &); +}; + +// XXX need CORBA_Object_var class typedef diff --git a/TAO/IIOP/lib/corba/orb.hh b/TAO/IIOP/lib/corba/orb.hh new file mode 100644 index 00000000000..ccb5ea69c29 --- /dev/null +++ b/TAO/IIOP/lib/corba/orb.hh @@ -0,0 +1,159 @@ +// @(#)orb.hh 1.3 95/09/13 + +/* + +COPYRIGHT AND LICENSING +----------------------- +This notice applies to all files in this software distribution. +Please read it! + + +Copyright 1995 Sun Microsystems, Inc. +Printed in the United States of America. +All Rights Reserved. + +This software product (LICENSED PRODUCT), implementing the Object Management +Group's "Internet Inter-ORB Protocol", is protected by copyright and is +distributed under the following license restricting its use. Portions of +LICENSED PRODUCT may be protected by one or more U.S. or foreign patents, or +pending applications. + +LICENSED PRODUCT is made available for your use provided that you include +this license and copyright notice on all media and documentation and the +software program in which this product is incorporated in whole or part. + +You may copy, modify, distribute, or sublicense the LICENCED PRODUCT without +charge as part of a product or software program developed by you, so long as +you preserve the functionality of interoperating with the Object Management +Group's "Internet Inter-ORB Protocol" version one. However, any uses other +than the foregoing uses shall require the express written consent of Sun +Microsystems, Inc. + +The names of Sun Microsystems, Inc. and any of its subsidiaries or +affiliates may not be used in advertising or publicity pertaining to +distribution of the LICENSED PRODUCT as permitted herein. + +This license is effective until terminated by Sun for failure to comply with +this license. Upon termination, you shall destroy or return all code and +documentation for the LICENSED PRODUCT. + +LICENSED PRODUCT IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING +THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR +PURPOSE, NONINFRINGEMENT, OR ARISING FROM A COURSE OF DEALING, USAGE OR +TRADE PRACTICE. + +LICENSED PRODUCT IS PROVIDED WITH NO SUPPORT AND WITHOUT ANY OBLIGATION ON +THE PART OF SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES TO ASSIST IN ITS +USE, CORRECTION, MODIFICATION OR ENHANCEMENT. + +SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES SHALL HAVE NO LIABILITY WITH +RESPECT TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY +LICENSED PRODUCT OR ANY PART THEREOF. + +IN NO EVENT WILL SUN OR ANY OF ITS SUBSIDIARIES OR AFFILIATES BE LIABLE FOR +ANY LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL +DAMAGES, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +Use, duplication, or disclosure by the government is subject to restrictions +as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and +Computer Software clause at DFARS 252.227-7013 and FAR 52.227-19. + +SunOS, SunSoft, Sun, Solaris, Sun Microsystems and the Sun logo are +trademarks or registered trademarks of Sun Microsystems, Inc. + +SunSoft, Inc. +2550 Garcia Avenue +Mountain View, California 94043 + +*/ + +// +// MASTER HEADER file for C/C++ Win32 interface to a CORBA 2.0 ORB. +// Include only this file, to see all ORB interfaces declared. +// +// This interface uses COM as the basis for its binary standard, and +// follows the OMG C++ mapping for compilers which don't support C++ +// exceptions (COM doesn't use them) or namespaces. Objects which +// are implemented using this source base support OLE Automation. +// +// XXX Note re status of this as reference, cosubmitted with RFP? +// +#ifndef _CORBA_orb_hh +#define _CORBA_orb_hh + +#include <stdlib.h> + +// +// For some reason, PC compilers don't implement "natural" alignment, but +// only use fixed alignment policies. The following #pragmas configure +// fixed one-byte alignment policy, since some fixed policy needs to apply +// throughout an ORB. +// +#if defined (_MSC_VER) +# pragma pack (push, 1) // VC++, stack 1-byte alignment policy + +# ifdef _DEBUG // convert from VC++ convention ... +# define DEBUG // ... to normal convention +# endif + +#elif defined (__BORLANDC__) +# pragma option -a // BC++, use 1 byte alignment + +#endif + +// +// Get various definitions facilitating portability. +// +#include <corba/orbconf.hh> + +// +// Forward declarations of some data types are needed. +// +typedef class CORBA_TypeCode *CORBA_TypeCode_ptr; +typedef class CORBA_Any *CORBA_Any_ptr; + +typedef class CORBA_ImplementationDef *CORBA_ImplementationDef_ptr; + +typedef class CORBA_InterfaceDef *CORBA_InterfaceDef_ptr; + +typedef class CORBA_Request *CORBA_Request_ptr; + +typedef class CORBA_NamedValue *CORBA_NamedValue_ptr; + +typedef class CORBA_NVList *CORBA_NVList_ptr; + +typedef unsigned CORBA_Flags; // enum values defined in nvlist.hh, + // bitwise ORed + +// +// Basic types used in the CORBA 2.0 runtime +// +#include <corba/corbacom.hh> +#include <corba/except.hh> +#include <corba/object.hh> +#include <corba/sequence.hh> +#include <corba/principa.hh> +#include <corba/typecode.hh> +#include <corba/any.hh> +#include <corba/nvlist.hh> +#include <corba/request.hh> +#include <corba/svrrqst.hh> + +// +// Bootstrapping, etc +// +#include <corba/orbobj.hh> + + +// NOTE: stub APIs are nonportable, and must be explicitly #included +// by code emitted from an IDL compiler. + + +#if defined (_MSC_VER) +# pragma pack (pop) // VC++, goes back to other padding rules +#endif // VC++ + + // BC++ we leave with 1-byte padding rules... + +#endif // _CORBA_orb_hh + diff --git a/TAO/IIOP/lib/corba/orbconf.hh b/TAO/IIOP/lib/corba/orbconf.hh new file mode 100644 index 00000000000..7c6906aa7ad --- /dev/null +++ b/TAO/IIOP/lib/corba/orbconf.hh @@ -0,0 +1,203 @@ +/* orbconf.hh. Generated automatically by configure. */ +// @(#)orbconf.hh 1.2 95/10/02 +// Copyright 1995 by Sun Microsystems, Inc +// +// Build configuration file for the Inter-ORB Engine, and application +// level code using it via <corba.hh> +// +// !! Modify "orbconf.hh", not the file generated by "configure" !! +// +// Keep the tests in the order they're found in "configure.in". Also, keep +// in mind that all "#undef" preprocessor directives are deemed to be the +// property of "autoconf"; if those are needed in their own right, they must +// be in some other file. +// +// This file builds on work originally contributed by a team at apm.co.uk: +// Owen Rees (rtor), Guangxing Li (gxl) and Mike Beasley (mdrb). +// + +#ifndef _ORB_CONFIG_HH +#define _ORB_CONFIG_HH + +/* Define if you have the <widec.h> header file. */ +#define HAVE_WIDEC_H 1 + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef pid_t */ + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#define WORDS_BIGENDIAN 1 + +/* The number of bytes in an int. */ +#define SIZEOF_INT 4 + +/* The number of bytes in a long. */ +#define SIZEOF_LONG 4 + +/* The number of bytes in a void *. */ +#define SIZEOF_VOID_P 4 + +/* The number of bytes in a long long. */ +#define SIZEOF_LONG_LONG 8 + +/* The number of bytes in a long double. */ +#define SIZEOF_LONG_DOUBLE 16 + +/* The number of bytes in a bool. */ +#define SIZEOF_BOOL 0 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you don't have vprintf but do have _doprnt. */ +/* #undef HAVE_DOPRNT */ + +/* Define if you have the vprintf function. */ +#define HAVE_VPRINTF 1 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the strdup function. */ +#define HAVE_STRDUP 1 + +/* Define if you have the popen function. */ +#define HAVE_POPEN 1 + +/* Define if SIG_IGN is broken. */ +/* #undef SIG_IGN_BROKEN */ + +/* Define if you have h_errno declared. */ +#define DECLARED_H_ERRNO 1 + +/* Define if you have accept declared. */ +#define DECLARED_ACCEPT 1 + +/* Define if you have bind declared. */ +#define DECLARED_BIND 1 + +/* Define if you have connect declared. */ +#define DECLARED_CONNECT 1 + +/* Define if you have listen declared. */ +#define DECLARED_LISTEN 1 + +/* Define if you have select declared. */ +#define DECLARED_SELECT 1 + +/* Define if you have setsockopt declared. */ +#define DECLARED_SETSOCKOPT 1 + +/* Define if you have shutdown declared. */ +#define DECLARED_SHUTDOWN 1 + +/* Define if you have socket declared. */ +#define DECLARED_SOCKET 1 + +/* Define if you have gettimeofday declared. */ +#define DECLARED_GETTIMEOFDAY 1 + +/* Define if you have kill declared. */ +#define DECLARED_KILL 1 + +/* Define if you have gethostname declared. */ +/* #undef DECLARED_GETHOSTNAME */ + +/* Define if you have bzero declared. */ +/* #undef DECLARED_BZERO */ + +/* Define if you have strerror declared. */ +#define DECLARED_STRERROR 1 + + + +// +// Various issues not dealt with via autoconf/configure. +// +#if !defined (_WINSOCKAPI_) + // Winsock added nonstandard APIs +# define closesocket(s) close(s) +#endif // _WINSOCKAPI_ + +#ifdef minor + // namespace pollution that's common on older UNIXes, + // XXX this can't go here, "autoconf" wants to own #undef +/* # undef minor */ +#endif // minor + + +// +// BC++ seems to have a different convention for detecting Win32 than VC++. +// +#if defined (__WIN32__) +# define _WIN32 +#endif // BC++ convention + +// +// For Win16, near/far pointers reflect same/other segment addressing. +// +#if defined (unix) || defined (_WIN32) +# define _FAR +#endif + +// +// Assume DOS/Windows if "configure" didn't get run. +// +#ifndef SIZEOF_LONG +# define SIZEOF_BOOL 0 +# ifdef _WIN32 +# define SIZEOF_INT 4 +# else // Win16 +# define SIZEOF_INT 2 +# endif // Win32/Win16 +# define SIZEOF_LONG 4 +# define SIZEOF_VOID_P 4 // "large model" or Win32 +# define SIZEOF_LONG_LONG 8 +# define SIZEOF_LONGDOUBLE 12 + +# define DECLARED_ACCEPT +# define DECLARED_BIND +# define DECLARED_CONNECT +# define DECLARED_GETHOSTNAME +# define DECLARED_H_ERRNO +# define DECLARED_LISTEN +# define DECLARED_LISTEN +# define DECLARED_SELECT +# define DECLARED_SETSOCKOPT +# define DECLARED_SHUTDOWN +# define DECLARED_SOCKET +# define DECLARED_STRERROR + +# define HAVE_STRDUP +# define HAVE_VPRINTF + +typedef unsigned long pid_t; + +#endif + +// +// For both Win16 and Win32, DLLs (shared libraries) need to know what +// classes, functions, and data are exported. DLLs aren't quite dealt +// with here yet, it's tough to do it portably (viz. the different +// answers now used by BC++/VC++). +// +// (Wouldn't it be nice if more C++ compilers and linkers took advantage +// of syntax to help get ensure that only public interfaces get exported +// from shared libraries? Hmmm...) +// +#if defined (unix) || !defined (__BORLANDC__) +# define _EXPCLASS +# define _EXPFUNC +# define _EXPDATA +#endif + +#if defined (hpux) +# define SELECT_INT_STAR +#endif + +#ifndef HAVE_STRDUP +#define strdup(s) strcpy ((char *)malloc (strlen (s) + 1), s) +#endif + +#endif // _ORB_CONFIG_HH diff --git a/TAO/IIOP/lib/corba/orbconf.hh.in b/TAO/IIOP/lib/corba/orbconf.hh.in new file mode 100644 index 00000000000..62b9c0949a8 --- /dev/null +++ b/TAO/IIOP/lib/corba/orbconf.hh.in @@ -0,0 +1,202 @@ +// @(#)orbconf.hh.in 1.2 95/09/06 +// Copyright 1995 by Sun Microsystems, Inc +// +// Build configuration file for the Inter-ORB Engine, and application +// level code using it via <corba.hh> +// +// !! Modify "orbconf.hh.in", not the file generated by "configure" !! +// +// Keep the tests in the order they're found in "configure.in". Also, keep +// in mind that all "#undef" preprocessor directives are deemed to be the +// property of "autoconf"; if those are needed in their own right, they must +// be in some other file. (For example, "#undef minor".) +// +// This file builds on work originally contributed by a team at apm.co.uk: +// Owen Rees (rtor), Guangxing Li (gxl) and Mike Beasley (mdrb). +// + +#ifndef _ORB_CONFIG_HH +#define _ORB_CONFIG_HH + +/* Define if you have the <widec.h> header file. */ +#undef HAVE_WIDEC_H + +/* Define to `int' if <sys/types.h> doesn't define. */ +#undef pid_t + +/* Define if your processor stores words with the most significant + byte first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* The number of bytes in an int. */ +#undef SIZEOF_INT + +/* The number of bytes in a long. */ +#undef SIZEOF_LONG + +/* The number of bytes in a void *. */ +#undef SIZEOF_VOID_P + +/* The number of bytes in a long long. */ +#undef SIZEOF_LONG_LONG + +/* The number of bytes in a long double. */ +#undef SIZEOF_LONG_DOUBLE + +/* The number of bytes in a bool. */ +#undef SIZEOF_BOOL + +/* Define as the return type of signal handlers (int or void). */ +#undef RETSIGTYPE + +/* Define if you don't have vprintf but do have _doprnt. */ +#undef HAVE_DOPRNT + +/* Define if you have the vprintf function. */ +#undef HAVE_VPRINTF + +/* Define if you have the gettimeofday function. */ +#undef HAVE_GETTIMEOFDAY + +/* Define if you have the strdup function. */ +#undef HAVE_STRDUP + +/* Define if you have the popen function. */ +#undef HAVE_POPEN + +/* Define if SIG_IGN is broken. */ +#undef SIG_IGN_BROKEN + +/* Define if you have h_errno declared. */ +#undef DECLARED_H_ERRNO + +/* Define if you have accept declared. */ +#undef DECLARED_ACCEPT + +/* Define if you have bind declared. */ +#undef DECLARED_BIND + +/* Define if you have connect declared. */ +#undef DECLARED_CONNECT + +/* Define if you have listen declared. */ +#undef DECLARED_LISTEN + +/* Define if you have select declared. */ +#undef DECLARED_SELECT + +/* Define if you have setsockopt declared. */ +#undef DECLARED_SETSOCKOPT + +/* Define if you have shutdown declared. */ +#undef DECLARED_SHUTDOWN + +/* Define if you have socket declared. */ +#undef DECLARED_SOCKET + +/* Define if you have gettimeofday declared. */ +#undef DECLARED_GETTIMEOFDAY + +/* Define if you have kill declared. */ +#undef DECLARED_KILL + +/* Define if you have gethostname declared. */ +#undef DECLARED_GETHOSTNAME + +/* Define if you have bzero declared. */ +#undef DECLARED_BZERO + +/* Define if you have strerror declared. */ +#undef DECLARED_STRERROR + + + +// +// Various issues not dealt with via autoconf/configure. +// + +#if !defined (_WINSOCKAPI_) + // Winsock added nonstandard APIs +# define closesocket(s) close(s) +#endif // _WINSOCKAPI_ +#ifdef minor + // some older UNIXes have namespace pollution +# undef minor +#endif // minor + + +// +// BC++ seems to have a different convention for detecting Win32 than VC++. +// +#if defined (__WIN32__) +# define _WIN32 +#endif // BC++ convention + +// +// For Win16, near/far pointers reflect same/other segment addressing. +// +#if defined (unix) || defined (_WIN32) +# define _FAR +#endif + +// +// Assume DOS/Windows if "configure" didn't get run. +// +#ifndef SIZEOF_LONG +# define SIZEOF_BOOL 0 +# ifdef _WIN32 +# define SIZEOF_INT 4 +# else // Win16 +# define SIZEOF_INT 2 +# endif // Win32/Win16 +# define SIZEOF_LONG 4 +# define SIZEOF_VOID_P 4 // "large model" or Win32 +# define SIZEOF_LONG_LONG 0 +# define SIZEOF_LONGDOUBLE 12 + +# define DECLARED_ACCEPT +# define DECLARED_BIND +# define DECLARED_CONNECT +# define DECLARED_GETHOSTNAME +# define DECLARED_H_ERRNO +# define DECLARED_LISTEN +# define DECLARED_LISTEN +# define DECLARED_SELECT +# define DECLARED_SETSOCKOPT +# define DECLARED_SHUTDOWN +# define DECLARED_SOCKET +# define DECLARED_STRERROR + +# define HAVE_STRDUP +# define HAVE_VPRINTF + +// XXX sometimes compiler gets confused about this one ... +typedef unsigned long pid_t; + +#endif // DOS/Windows + +// +// For both Win16 and Win32, DLLs (shared libraries) need to know what +// classes, functions, and data are exported. DLLs aren't quite dealt +// with here yet, it's tough to do it portably (viz. the different +// answers now used by BC++/VC++). +// +// (Wouldn't it be nice if more C++ compilers and linkers took advantage +// of syntax to help get ensure that only public interfaces get exported +// from shared libraries? Hmmm...) +// +#if defined (unix) || !defined (__BORLANDC__) +# define _EXPCLASS +# define _EXPFUNC +# define _EXPDATA +#endif + +#if defined (hpux) +# define SELECT_INT_STAR +#endif + +#ifndef HAVE_STRDUP +#define strdup(s) strcpy ((char *)malloc (strlen (s) + 1), s) +#endif + +#endif // _ORB_CONFIG_HH diff --git a/TAO/IIOP/lib/corba/orbobj.hh b/TAO/IIOP/lib/corba/orbobj.hh new file mode 100644 index 00000000000..960fcac5b8e --- /dev/null +++ b/TAO/IIOP/lib/corba/orbobj.hh @@ -0,0 +1,72 @@ +// +// Header file for Win32 interface to CORBA's "ORB" type. +// +// The "ORB" pseudo-object is used in bootstrapping, such as to +// create object references from strings. It's also used to +// create strings from object references. +// +typedef class CORBA_ORB *CORBA_ORB_ptr; +void CORBA_release (CORBA_ORB_ptr orb); +CORBA_Boolean CORBA_is_nil (CORBA_ORB_ptr orb); + +extern const IID IID_CORBA_ORB; + +// +// ORB initialisation, per OMG document 94-9-46. +// +CORBA_ORB_ptr +CORBA_ORB_init ( + int &argc, + char *const *argv, + char *orb_name, + CORBA_Environment &env +); + +// +// ORB pseudo-objref +// +class _EXPCLASS CORBA_ORB : public IUnknown +{ + public: + static CORBA_ORB_ptr _duplicate (CORBA_ORB_ptr orb); + static CORBA_ORB_ptr _nil (); + + virtual CORBA_Object_ptr string_to_object ( + CORBA_String str, + CORBA_Environment &env + ) = 0; + virtual CORBA_String object_to_string ( + CORBA_Object_ptr obj, + CORBA_Environment &env + ) = 0; + + // similar for TypeCodes and Anys ... to/from octet sequences + + void create_list ( + CORBA_Long count, + CORBA_NVList_ptr &retval + ); + + // + // Stuff required for COM IUnknown support ... this class is intended + // to be inherited by others, which will provide some more of the + // CORBA/COM support. Implementations of this "CORBA_ORB" class must + // know how to create stringify/destringify their objrefs, as well as + // how to marshal and unmarshal them ... as well as provide their + // own QueryInterface. + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + + protected: + CORBA_ORB (); + virtual ~CORBA_ORB (); + + private: + unsigned _refcount; + + // these are not provided + CORBA_ORB (const CORBA_ORB &); + CORBA_ORB &operator = (const CORBA_ORB &); +}; + diff --git a/TAO/IIOP/lib/corba/principa.hh b/TAO/IIOP/lib/corba/principa.hh new file mode 100644 index 00000000000..3cb5b0a8880 --- /dev/null +++ b/TAO/IIOP/lib/corba/principa.hh @@ -0,0 +1,52 @@ +// +// Header file for Win32 interface to CORBA's "Principal" type. +// +// A "Principal" identifies an authenticated entity in the network +// administration framework. Identities are used to control acccess +// (authorization) as well as in audit trails (accountability). +// +typedef class CORBA_Principal *CORBA_Principal_ptr; + +void CORBA_release (CORBA_Principal_ptr principal); +CORBA_Boolean CORBA_is_nil (CORBA_Principal_ptr principal); + +extern const IID IID_CORBA_Principal; + +class _EXPCLASS CORBA_Principal : public IUnknown +{ + public: + // + // To applications, the identifier is an opaque ID. + // + CORBA_SEQUENCE <CORBA_Octet> id; + + // XXX add "==", "<", ">" operators + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + CORBA_Principal (); + private: + unsigned _refcount; + + virtual ~CORBA_Principal (); + + // these are not provided + CORBA_Principal &operator = (const CORBA_Principal_ptr &); + CORBA_Principal (const CORBA_Principal_ptr &); + +#if defined (__GNUG__) + // + // G++ (even 2.6.3) stupidly thinks instances can't be + // created. This de-warns. + // + friend class everyone_needs_a_friend; +#endif +}; diff --git a/TAO/IIOP/lib/corba/request.hh b/TAO/IIOP/lib/corba/request.hh new file mode 100644 index 00000000000..74e6e31afba --- /dev/null +++ b/TAO/IIOP/lib/corba/request.hh @@ -0,0 +1,69 @@ +// @(#)request.hh 1.2 95/10/02 +// Copyright 1994-1995 by Sun Microsystems, Inc. +// +// Header file for Win32 C/C++/COM interface to CORBA's Dynamic +// Invocation Interface "Request" type. +// + +void CORBA_release (CORBA_Request_ptr req); +CORBA_Boolean CORBA_is_nil (CORBA_Request_ptr req); + +typedef CORBA_SEQUENCE <CORBA_TypeCode_ptr> CORBA_ExceptionList; +typedef CORBA_ExceptionList *CORBA_ExceptionList_ptr; + +extern const IID IID_CORBA_Request; + +class _EXPCLASS CORBA_Request : public IUnknown +{ + public: + // + // XXX these should not be inlined + // + CORBA_Object_ptr target () const { return _target; } + const CORBA_Char *operation () const { return _opname; } + CORBA_NVList_ptr arguments () { return _args; } + CORBA_NamedValue_ptr result () { return _result; } + CORBA_ExceptionList_ptr exceptions () { return &_exceptions; } + CORBA_Environment *env () { return &_env; } + + void invoke (); + void send_oneway (); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + + private: + friend class CORBA_Object; + + CORBA_Request ( + CORBA_Object_ptr obj, + const CORBA_Char *op, + CORBA_NVList_ptr args, + CORBA_NamedValue_ptr result, + CORBA_Flags flags + ); + + CORBA_Request ( + CORBA_Object_ptr obj, + const CORBA_Char *op + ); + + virtual ~CORBA_Request (); + + CORBA_Object_ptr _target; + const CORBA_Char *_opname; + CORBA_NVList_ptr _args; + CORBA_NamedValue_ptr _result; + CORBA_Flags _flags; + CORBA_Environment _env; + CORBA_ExceptionList _exceptions; + + unsigned _refcount; +}; diff --git a/TAO/IIOP/lib/corba/sequence.hh b/TAO/IIOP/lib/corba/sequence.hh new file mode 100644 index 00000000000..533bdf2b617 --- /dev/null +++ b/TAO/IIOP/lib/corba/sequence.hh @@ -0,0 +1,23 @@ +// +// utility template class +// +template <class T> +struct CORBA_SEQUENCE { +#if SIZEOF_LONG == 4 + unsigned long maximum; + unsigned long length; +#else + // just assume "int" is four bytes long ... + unsigned maximum; + unsigned length; +#endif + T *buffer; + + CORBA_SEQUENCE () + : maximum (0), length (0), buffer (0) { } + + // XXX destructor should free buffer, elements!! + ~CORBA_SEQUENCE () { } +}; + +typedef CORBA_SEQUENCE <CORBA_Octet> CORBA_OctetSeq; diff --git a/TAO/IIOP/lib/corba/stub.hh b/TAO/IIOP/lib/corba/stub.hh new file mode 100644 index 00000000000..ee6bf803109 --- /dev/null +++ b/TAO/IIOP/lib/corba/stub.hh @@ -0,0 +1,226 @@ +// @(#)stub.hh 1.7 95/09/12 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB data structures used by static and dynamic stubs, +// and to a lesser degree by skeletons +// +// Header file for Win32 C/C++/COM interface to one kind of CORBA remote +// invocation framework. This is for use by compiler-generated code, not +// by portable applications! +// +// These constitute the stub API to this "ORB Core". Such interfaces are +// not specified by OMG, and may differ between different ORBs. This +// one has the particular advantage that stubs can be quite small. +// +#ifndef _STUB_HH +#define _STUB_HH + +// +// Descriptions of parameters. +// +enum param_type { // parameter mode of a given parameter + PARAM_IN, + PARAM_OUT, + PARAM_INOUT, + PARAM_RETURN // = PARAM_OUT +}; + +struct paramdata { // description of a single parameter + CORBA_TypeCode_ptr tc; // type of param + param_type mode; // its mode + + // + // If value_size is nonzero for OUT, INOUT, or RETURN parameters, it's + // (a) an indicator that the ORB returns a pointer-to-value for this + // parameter, and also (b) is the size of the top-level of the value + // that's returned (e.g. ignoring nested sequence buffers). That is, + // it moves CPU cycles from runtime -- some calls to tc->size() -- to + // compile time where they're cheap/free. + // + // It _must_ only be set for cases where the ORB allocates the return + // value, which must then be ORB::free()d ... e.g. where the value + // is a pointer to data such as a struct, sequence, or union. (The + // CORBA C++ mapping doesn't require that for all "out" structs; only + // those of "variable size".) If this value is nonzero, the value + // passed to do_call() must be the address of a pointer. + // + size_t value_size; // zero or tc->size () +}; + + +// +// Descriptions of operations, as used by the stub interpreter. Only +// interpretive marshaling/unmarshaling is used, and the stubs don't +// know what particular on-the-wire protocol is being used. +// +// NOTE: when using C++ exceptions, many C++ compilers will require +// the use of compiled code throw the exception. As binary standards +// for exception throwing evolve, it may become practical to interpretively +// throw exceptions. +// +struct calldata { + const char *opname; // operation name + CORBA_Boolean is_roundtrip; // !oneway + + // + // When constructing tables of parameters, put them in the same order + // they appear in the IDL spec: return value, then parameters left + // to right. Other orders may produce illegal IIOP protocol messages. + // + unsigned param_count; // # parameters + const paramdata *params; // their descriptions + + // + // The exceptions allowed by this operation can be listed in any + // order, and need not include the system exceptions which may be + // returned by OMG-IDL operations. If an operation tries to return + // any exception not allowed by its type signature, this causes a + // a system exception to be reported. + // + unsigned except_count; // # exceptions + const CORBA_TypeCode_ptr *excepts; // their descriptions +}; + + +// +// Skeletons map "ServerRequest" generic signatures to the static call +// signature required by the implementation's methods. +// +// NOTE: there are several optimizations that'd be desirable for use by +// "static skeletons", notably (a) passing of per-object data held by the +// OA so that the method doesn't need to look it up itself, (b) removing +// all mandatory heap allocation of data, and of course (c) handling all +// the built-in ORB calls like "is_a" and "get_implementation". This code +// is currently set up only for Dynamic Skeletons and bridging, for which +// none of those are real issues. +// +typedef void (*skeleton)(CORBA_ServerRequest &, CORBA_Environment &); + +struct skel_entry { // table of these per implementation + const calldata *op_descriptor; + skeleton impl_skeleton; +}; + + + +// +// Per-objref data includes the (protocol-specific) ProfileBody, which +// is handled by placing it into a subclass of this type along with +// data that may be used in protocol-specific caching schemes. +// +// The type ID (the data specified by CORBA 2.0 that gets exposed "on +// the wire", and in stringified objrefs) is held by this module. +// +// The stub and DII interpreter APIs are member functions of this type. +// +extern const IID IID_STUB_Object; + +class _EXPCLASS STUB_Object : public IUnknown +{ + public: + // + // The "stub interpreter" method parameters are: + // + // - env ... used for exception reporting + // - info ... describes the call + // - varargs parameters follow + // + // The varargs parameters are pointers to data instances as described + // by info->params. (If the value_size is nonzero, the data instance + // is itself a pointer.) The order on the call stack must be exactly + // the order they're listed in info->params; normally this is the order + // the values are listed in the OMG-IDL operation specification. + // + // NOTE: This can be sped up by eliminating some parameter shuffling. + // The stub and "do_call" parameters are all but the same, except that + // their order is different. + // + virtual void do_call ( + CORBA_Environment &env, + const calldata *info, + ... + ) = 0; + + // + // Dynamic invocations use a more costly "varargs" calling convention; + // it's got the same input data as the (static) stub-oriented one, but + // the data is represented somewhat differently. + // + // Operation-specific data isn't collected in a stack frame and into a + // static/readonly "calldata" structure, but instead is collected into + // these parameters, the majority of which are heap-allocated: + // + // - opname ... the name of the operation + // - is_roundtrip ... true except for oneway operations, or ones where + // the client doesn't care about responses + // - args ... call parameters and their descriptions + // - result ... result and its description + // - flags ... only one DII flag is legal + // - exceptions ... list of legal user-defined exceptions + // - env ... used for exception reporting. + // + virtual void do_dynamic_call ( + const char *opname, + CORBA_Boolean is_roundtrip, + CORBA_NVList_ptr args, + CORBA_NamedValue_ptr result, + CORBA_Flags flags, + CORBA_ExceptionList &exceptions, + CORBA_Environment &env + ) = 0; + + // + // All objref representations carry around a type ID. + // + CORBA_String type_id; + + // + // All objref representations know how to hash themselves and compare + // themselves for equivalence to others. It's easily possible to have + // two objrefs that are distinct copies of data that refers/points to + // the same remote object (i.e. are equivalent). + // + virtual CORBA_ULong hash ( + CORBA_ULong maximum, + CORBA_Environment &env + ) = 0; + virtual CORBA_Boolean is_equivalent ( + CORBA_Object_ptr other_obj, + CORBA_Environment &env + ) = 0; + + // + // XXX All objref representations should know how to marshal themselves. + // That will involve ensuring that the IOR that gets marshaled talks a + // specific protocol, otherwise the target of a message would not be + // invoke using the objref it receives (compromising functionality in + // a very basic and mysterious manner). So for example an objref might + // need to create a proxy for itself rather than marshaling its own + // representation. [ The IIOP engine does not need to worry about such + // issues since it only supports one protocol -- the problem won't show + // up. "Multiprotocol ORBs" will need to solve that problem though. ] + // + + STUB_Object (char *p = 0) + : type_id (p) { } + + protected: + // XXX virtual inlines are evil + virtual ~STUB_Object () + { CORBA_string_free (type_id); } + + // + // COM operations ... provided by an implementation class which + // inherits from this one. + // + + private: + // + // Disallow copy constructor and assignment operator + // + STUB_Object (const STUB_Object &); + operator = (const STUB_Object &); +}; + +#endif //_STUB_HH diff --git a/TAO/IIOP/lib/corba/svrrqst.hh b/TAO/IIOP/lib/corba/svrrqst.hh new file mode 100644 index 00000000000..784b1b7c4de --- /dev/null +++ b/TAO/IIOP/lib/corba/svrrqst.hh @@ -0,0 +1,73 @@ +// @(#)svrrqst.hh 1.5 95/09/25 +// +// Header file for Win32 C/C++/COM interface to CORBA's Dynamic +// Server Skeleton Interface's "Server Request" type. +// +// XXX this is currently in an intermediate state; this is not +// supposed to be IIOP-specific, or to expose quite so many +// implementation details, but right now it is. +// + +typedef class CORBA_ServerRequest *CORBA_ServerRequest_ptr; +typedef class CORBA_ORB *CORBA_ORB_ptr; +typedef class TOA *TOA_ptr; + +void CORBA_release (CORBA_ServerRequest_ptr req); +CORBA_Boolean CORBA_is_nil (CORBA_ServerRequest_ptr req); + +extern const IID IID_CORBA_ServerRequest; + +class _EXPCLASS CORBA_ServerRequest : public IUnknown +{ + public: + // + // Implementation uses this to provide the ORB with the operation's + // parameter list ... on return, their values are available; the list + // fed in has typecodes and (perhap) memory assigned. + // + virtual void __stdcall params ( + CORBA_NVList_ptr list, + CORBA_Environment &env + ) = 0; + + // + // Implementation uses this to provide the operation result ... illegal + // if exception() was called or params() was not called. + // + // XXX Implementation should be able to assume response has been sent + // when this returns, and reclaim memory it allocated. + // + virtual void __stdcall result ( + CORBA_Any_ptr value, + CORBA_Environment &env + ) = 0; + + // + // Implementation uses this to provide the exception value which is the + // only result of this particular invocation. + // + // XXX Implementation should be able to assume response has been sent + // when this returns, and reclaim memory it allocated. + // + virtual void __stdcall exception ( + CORBA_ExceptionType type, + CORBA_Any_ptr value, + CORBA_Environment &env + ) = 0; + + // + // Get various universal call attributes: who made the call, the + // target of the call, what ORB and OA that target object uses. + // + // NOTE: none of these report exceptions; unavailability of any of + // this stuff is a catastrophic error since this is all part of + // the basic CORBA Object Model. + // + // XXX should not be not assuming all OAs implement the TOA API !! + // + virtual CORBA_Principal_ptr __stdcall caller () = 0; + virtual CORBA_Object_ptr __stdcall target () = 0; + virtual CORBA_String __stdcall op_name () = 0; + virtual TOA_ptr __stdcall oa () = 0; + virtual CORBA_ORB_ptr __stdcall orb () = 0; +}; diff --git a/TAO/IIOP/lib/corba/toa.hh b/TAO/IIOP/lib/corba/toa.hh new file mode 100644 index 00000000000..23229870da2 --- /dev/null +++ b/TAO/IIOP/lib/corba/toa.hh @@ -0,0 +1,182 @@ +// @(#)toa.hh 1.6 95/10/02 +// Copyright 1995 by Sun Microsystems, Inc. +// +// (Early) TOA +// +// TOA is a stripped down, lean, mean, portable OA. The work involved +// in managing objects is all handled by "higher level" code, including +// skeletons, generated either by an IDL compiler by hand. TOA itself +// maintains no object-level state. +// + +#ifndef _TOA_HH +#define _TOA_HH + +typedef class TOA *TOA_ptr; + +CORBA_Boolean is_nil (TOA_ptr obj); +void release (TOA_ptr obj); + +extern const IID IID_TOA; + +class TOA : public IUnknown +{ + public: + // + // Create a reference to an object, using identifying information that + // is fully exposed to applications. (An ORB may use additional data + // internally, of course.) + // + // Object IDs are assigned and used by servers to identify objects. + // + // Type IDs are repository IDs, assigned as part of OMG-IDL interface + // definition to identify specific interfaces and their relationships + // to other OMG-IDL interfaces. It's OK to provide a null type ID. + // + // Clients which invoke operations using one of these references when + // the server is not active (or after the last reference to the TOA is + // released) will normally see an OBJECT_NOT_EXIST exception reported + // by the ORB. If the TOA is a "Named TOA" the client's ORB will not + // normally return OBJECT_NOT_EXIST unless the TOA reports that fault. + // + // NOTE: Since any given TOA may have been used in the past, servers + // may need to place some data (such as a timestamp) into the object ID + // to help distinguish different incarnations of the TOA. "Named TOA" + // objects won't want those semantics as much as "Anonymous" ones. + // + virtual CORBA_Object_ptr __stdcall + create ( + CORBA_OctetSeq &obj_id, + CORBA_String type_id, + CORBA_Environment &env + ) = 0; + + // + // All invocations are handled using DSI ... slightly enhanced from + // the original CORBA 2.0 specs, to improve performance by getting + // rid of all mallocation for calls with fixed-size parameter lists. + // + // A single skeleton, also called "Dynamic Implementation Routine", + // is provided to the ORB; it is called on all requests, along with a + // pointer to context that was provided by the server. + // + // One could imagine that the DIR would recognize that the context + // is a hashtable for per-object state, keyed by the object ID. + // + // Note that in addition to the operations defined by an object's + // IDL interface specification, four operations must be supported + // by code layered above the TOA. There are many ways in which + // these operations can be hidden from "application" programs, and + // some solutions are noted below. + // + // * "_is_a" is readily handled by skeletons, + // + // * "_get_interface" similarly, though with more work to allow the + // IFR data structures to be extracted from skeletons. + // + // * "_get_implementation" is implementation-specific, a facility + // through which administrative and other information may be + // acquired. Not all systems provide consistent ways to utilize + // this facility. + // + // * "_non_existent" asks if the referred-to object still exists. + // This enables solving many "distributed garbage" problems, + // such as maintaining persistent tables keyed by references to + // objects that may no longer exist. + // + typedef void __stdcall (*dsi_handler) ( // DIR + CORBA_OctetSeq &obj_id, + CORBA_ServerRequest &request, + void *context, + CORBA_Environment &env + ); + + virtual void __stdcall register_dir ( + dsi_handler skeleton, + void *context, + CORBA_Environment &env + ) = 0; + + // + // Get/handle requests ... this starts processing a request, or + // times out. + // + // If the "use threads" flag is FALSE, then any request processing + // will have completed when the call returns. + // + // If the "use threads" flag is TRUE, then applications may see + // concurrent execution of method code (and processing may not be + // complete when this call returns). This value is only legal in + // environments which support threads. + // + // Normal timeout semantics apply: if the timeval pointer is NULL + // the call will not time out. Otherwise the value pointed to + // is the minimum amount of time that will elapse before this + // call returns. + // + // Reports INITIALIZE exception if no DIR was registered. + // Reports BAD_INV_ORDER if this is shutting down. + // + virtual void __stdcall get_request ( + CORBA_Boolean use_threads, + struct timeval *tvp, + CORBA_Environment &env + ) = 0; + + // + // Please Shutdown -- reject all further incoming requests, and allow + // all currently active calls (e.g. "this one") to complete. This + // ensures that OS resources associated with this OA can be reclaimed + // even if some buggy applications code mismanages refcounting on + // this TOA. + // + virtual void __stdcall please_shutdown ( + CORBA_Environment &env + ) = 0; + + // + // Run -- call get_request() in a loop until shutdown completes. + // Uses as much concurrency as is provided in this environment. + // Initiate shutdown if we've been idle for the specified time. + // + // This uses only the public APIs defined above; the function is + // defined here purely for convenience, to help some applications + // avoid writing that loop. + // + void __stdcall run ( + struct timeval *tvp, + CORBA_Environment &env + ); // NOT PURE VIRTUAL !! + + // + // Get an "anonymous" TOA pseudo-objref ... this is the API that + // most applications will use. It returns a TOA which is not + // otherwise in use (though it may have been used in the past). + // + // Any given TOA (named or otherwise) will create equivalent object + // references when TOA::create() is called with the same object and + // type IDs. This is not true for two different TOAs. + // + static TOA_ptr get_toa ( + CORBA_ORB_ptr orb, + CORBA_Environment &env + ); // NOT PURE VIRTUAL !! + + // + // Get a "named" TOA ... most applications don't use/need this API. + // + // TOA names are for ORB/system bootstrapping purposes, and need not + // be shared between different systems. The scope of the name isn't + // guaranteed to include more than one system. The names themselves + // are administered using system-specific mechanisms and policies. + // + static TOA_ptr get_named_toa ( + CORBA_ORB_ptr orb, + CORBA_String name, + CORBA_Environment &env + ); // NOT PURE VIRTUAL !! + + private: +}; + +#endif // _TOA_HH diff --git a/TAO/IIOP/lib/corba/typecode.hh b/TAO/IIOP/lib/corba/typecode.hh new file mode 100644 index 00000000000..17c4bb72cc5 --- /dev/null +++ b/TAO/IIOP/lib/corba/typecode.hh @@ -0,0 +1,299 @@ +// +// Header file for Win32 C/C++/COM interface to CORBA's "TypeCode" type. +// + +// +// Kinds of typecodes. Do not change these enum values, or duplicate +// them if you need to add values. They are used to index tables, and +// if you change the values you'll need to find and update all of those +// tables. The values are also part of the Common Data Representation, +// and hence are part of IIOP and other ORB protocols. +// +enum CORBA_TCKind { + tk_null = 0, + tk_void = 1, + tk_short = 2, + tk_long = 3, + tk_ushort = 4, + tk_ulong = 5, + tk_float = 6, + tk_double = 7, + tk_boolean = 8, + tk_char = 9, + tk_octet = 10, + tk_any = 11, + tk_TypeCode = 12, + tk_Principal = 13, + tk_objref = 14, + tk_struct = 15, + tk_union = 16, + tk_enum = 17, + tk_string = 18, + tk_sequence = 19, + tk_array = 20, + tk_alias = 21, // 94-11-7 + tk_except = 22, // 94-11-7 + + // these five are OMG-IDL data type extensions + tk_longlong = 23, // 94-9-32 Appendix A (+ 2) + tk_ulonglong = 24, // 94-9-32 Appendix A (+ 2) + tk_longdouble = 25, // 94-9-32 Appendix A (+ 2) + tk_wchar = 26, // 94-9-32 Appendix A (+ 2) + tk_wstring = 27, // 94-9-32 Appendix A (+ 2) + + // + // This symbol is not defined by CORBA 2.0. It's used to speed up + // dispatch based on TCKind values, and lets many important ones + // just be table lookups. It must always be the last enum value!! + // + TC_KIND_COUNT +}; + + +// +// Two "user exceptions" are defined for manipulating TypeCodes. +// +extern CORBA_TypeCode_ptr _tc_CORBA_Bounds; +class CORBA_Bounds : public CORBA_UserException { + public: + CORBA_Bounds ( + ) : CORBA_UserException (_tc_CORBA_Bounds) + {} +}; + +extern CORBA_TypeCode_ptr _tc_CORBA_BadKind; +class CORBA_BadKind : public CORBA_UserException { + public: + CORBA_BadKind ( + ) : CORBA_UserException (_tc_CORBA_BadKind) + {} +}; + + +// +// A TypeCode describes data. This one's as thin a wrapper around CDR +// octet sequences as is practical. There are guesses here about how +// the OMG C++ mapping and CORBA 2.0 IFR specification will interact. +// +// NOTE: Use TypeCode_ptr, except in code (e.g. output of and OMG-IDL +// compiler) that needs to create typecodes from their octet-sequence +// encodings. +// +typedef class CORBA_TypeCode *CORBA_TypeCode_ptr; +void CORBA_release (CORBA_TypeCode_ptr); +CORBA_Boolean CORBA_is_nil (CORBA_TypeCode_ptr obj); + +extern const IID IID_CORBA_TypeCode; + +class _EXPCLASS CORBA_TypeCode : public IUnknown +{ + public: + // + // For all TypeCode kinds + // + CORBA_TCKind kind (CORBA_Environment &) const; + + // + // Deprecated, CORBA 1.2, not fully usable + // + CORBA_ULong param_count (CORBA_Environment &) const; + + // + // For tk_{objref,struct,union,enum,alias,except} + // + CORBA_String id (CORBA_Environment &) const; + + // + // Other CORBA 2.0 IFR updates -- not yet implemented/needed + // + // String name () raises (BadKind); + // ULong member_count () raises (BadKind); + // String member_name (...) raises (BadKind, Bounds); + // TypeCode_ptr member_type (...) raises (BadKind, Bounds); + + CORBA_Any_ptr member_label (CORBA_ULong n, CORBA_Environment&) const; + + // TypeCode_ptr discriminator_type () raises (BadKind); + // Long default_index () raises (BadKind); + + // Long length () raises (BadKind); + // TypeCode_ptr content_type () raises (BadKind); + + // + // Internal utilities, pending CORBA 2.0 IFR APIs; just enough + // to make array and sequence typecode interpretation cheap + // + CORBA_ULong ulong_param (CORBA_ULong n, CORBA_Environment &) const; + CORBA_TypeCode_ptr typecode_param (CORBA_ULong n, + CORBA_Environment &) const; + + // + // Creation/refcounting ... these aren't really public APIs, + // but an IDL compiler will need to be able to create TypeCodes + // as part of creating stubs. + // + // + // This constructor is used only for built-in + // TypeCode constants, with no parameters. + // + CORBA_TypeCode ( + CORBA_TCKind kind // EMPTY paramlists! + ); + + // + // This constructor is used both for typecode + // constants and for heap-allocated TypeCodes. + // The two are distinguished by the orb_owns_tc + // flag passed in by the creator. + // + // For simple param lists with a single + // numeric parameter, only 'length' matters. + // + // For complex param lists, or simple param + // lists for which the parameter is a string + // or typecode, length _and_ buffer matter. + // + CORBA_TypeCode ( + CORBA_TCKind kind, + CORBA_ULong length, + CORBA_Octet *buffer, + CORBA_Boolean orb_owns_tc + ); + void *operator new (size_t, void *p) + { return p; } + void *operator new (size_t s) + { return ::operator new(s); } + + virtual ~CORBA_TypeCode (); + + // + // "orb owns" is always set, except for TypeCode constants. + // + + // + // This routine calls visit() on each component of one (or two) + // structurally equivalent data values. "Components" are either + // primitive (long, string, ...) or constructed (struct, ...) + // data elements. + // + // It will NOT descend into those nodes if they're constructed; + // it's the job of the visit() routine to do that as needed. + // + // "Context" can be used to hold state used by the visit() routine. + // To terminate traversal "early", visit() returns TRAVERSE_STOP. + // + // The "value1" and "value2" parameters are pointers to data + // values of the structure described by the TypeCode. Using + // the normal size, alignment, and padding rules used by the + // compilers on a given platform, the visit() routine is called + // with pointers to subsidiary elements. + // + // As all this routine does is appropriate pointer adjustments, + // it any value at all can be passed in as "value1" or "value2". + // You could ignore one value and examine a data structure; copy + // from one to the other; compare one to the other; and more. + // + // Normal usage is to have application code call its visit() + // routine directly, and have that decide whether to use the + // typecode interpereter's knowledge of data structure layout + // through mutual recursion. + // + enum traverse_status { TRAVERSE_STOP, TRAVERSE_CONTINUE }; + + typedef traverse_status (_FAR * VisitRoutine) ( + CORBA_TypeCode_ptr tc, + const void *value1, + const void *value2, + void *context, + CORBA_Environment &env + ); + + traverse_status traverse ( + const void *value1, + const void *value2, + VisitRoutine visit, + void *context, + CORBA_Environment &env + ); + + size_t size (CORBA_Environment &env); + size_t alignment (CORBA_Environment &env); + + static CORBA_TypeCode_ptr _nil (); + + // + // Stuff required for COM IUnknown support + // + ULONG __stdcall AddRef (); + ULONG __stdcall Release (); + HRESULT __stdcall QueryInterface ( + REFIID riid, + void **ppv + ); + // private: + // + // the guts of the typecode implementation class: a counted + // set of bytes, in marshaled CDR format. + // + CORBA_ULong _length; + CORBA_Octet *_buffer; + CORBA_TCKind _kind; + + // + // Indirected typecodes share "buffer" with a parent, and + // hold a reference to that parent to ensure its memory is + // not freed inappropriately. + // + CORBA_TypeCode_ptr _parent; + + private: + unsigned _refcount; + + // + // If "orb_owns" is false, the value is a constant typecode with + // both the typecode and the buffer statically allocated; the + // typecode is never freed. Otherwise the typecode and the + // buffer are freed when the refcount goes to zero. + // + CORBA_Boolean _orb_owns; + + // + // No copy constructor or assignment operator supported; + // use TypeCode_ptr values, duplicate(), release(). + // + CORBA_TypeCode (const CORBA_TypeCode &src); + CORBA_TypeCode &operator = (const CORBA_TypeCode &src); +}; + +// +// TypeCode constants, which are always accessible in all ORB runtimes. +// +extern const CORBA_TypeCode_ptr + _tc_CORBA_Null, + _tc_CORBA_Void, + + _tc_CORBA_Short, + _tc_CORBA_Long, + _tc_CORBA_LongLong, + _tc_CORBA_UShort, + _tc_CORBA_ULong, + _tc_CORBA_ULongLong, + + _tc_CORBA_Float, + _tc_CORBA_Double, + _tc_CORBA_LongDouble, + + _tc_CORBA_Boolean, + _tc_CORBA_Octet, + + _tc_CORBA_Char, + _tc_CORBA_String, + _tc_CORBA_WChar, + _tc_CORBA_WString, + + _tc_CORBA_Any, + _tc_CORBA_TypeCode, + _tc_CORBA_Principal, + + _tc_CORBA_Object; + diff --git a/TAO/IIOP/lib/onc/Makefile b/TAO/IIOP/lib/onc/Makefile new file mode 100644 index 00000000000..9848e24c388 --- /dev/null +++ b/TAO/IIOP/lib/onc/Makefile @@ -0,0 +1,8 @@ +# @(#)Makefile 1.1 @(#)Makefile 1.1 + +FILES = \ + xdr.cpp + +ROOT = ../.. + +include ../Makefile.gen diff --git a/TAO/IIOP/lib/onc/xdr.cpp b/TAO/IIOP/lib/onc/xdr.cpp new file mode 100644 index 00000000000..9c7c94f3eef --- /dev/null +++ b/TAO/IIOP/lib/onc/xdr.cpp @@ -0,0 +1,812 @@ +// @(#)xdr.cpp 1.3 95/11/07 +// Copyright 1995 by Sun Microsystems, Inc. +// +// XDR/TCP ORB Marshaler/unmarshaler +// +// Includes functions to encode 64 and 128 bit quantities, interpretive +// encoder/decoder. +// +// XXX note -- this is incomplete (see xdr.hh) and doesn't do +// exactly what the Prelude protocol does, particularly in the +// area of typecodes. +// +// XXX implement the put/get primitives for 64 and 128 bit data types +// +// XXX strings, principals, typecodes, char arrays, char seqs, and +// same for octets ... all should marshal the bulk characters tightly +// packed, four to a word, else this violates the XDR spec. +// +// XXX optimization 1: allowed by current XDR spec, when marshaling +// true bulk data could eliminate a buffer copy by writing long +// buffers (bigger than 'remainder of current fragment') in single +// large chunks ... weigh cost of an extra kernel write against data +// copy getting added. Applies to byte-structured bulk data only, +// not (portably) to arrays of longs etc. +// +// XXX optimization 2: modifies current XDR spec: when marshaling +// arrays of "short" data (e.g. unicode strings) pack tightly. +// + +#include <unistd.h> +#include <string.h> +#include <widec.h> + +#include <corba/orb.hh> + +#include "runtime/debug.hh" + +#include "onc/xdr.hh" + + +// +// I/O for 64 bit quantities -- integers, doubles +// +CORBA_Boolean +XDR_stream::put_longlong (const CORBA_LongLong &) +THROWS_NOTHING +{ + return CORBA_B_FALSE; +} + +CORBA_Boolean +XDR_stream::get_longlong (CORBA_LongLong &) +THROWS_NOTHING +{ + return CORBA_B_FALSE; +} + + +// +// I/O for 128 bit quantities -- long doubles +// +CORBA_Boolean +XDR_stream::put_longdouble (const CORBA_LongDouble &) +THROWS_NOTHING +{ + return CORBA_B_FALSE; +} + +CORBA_Boolean +XDR_stream::get_longdouble (CORBA_LongDouble &) +THROWS_NOTHING +{ + return CORBA_B_FALSE; +} + + +// +// Encode instances of arbitrary data types based only on typecode. "data" +// points to the data type; if it's not a primitve data type, the TypeCode +// interpreter is used to recursively encode its components. "context" is +// the marshaling stream on which to encode the data value. +// +// This is a fairly typical TypeCode interpreter visit() routine; it works +// on a single data value in conjunction with context information, and must +// handle all IDL data types. +// +CORBA_TypeCode::traverse_status +XDR_stream::encoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env +) +THROWS_NOTHING +{ + CORBA_Boolean continue_encoding = CORBA_B_TRUE; + XDR_stream *stream = (XDR_stream *)context; + + switch (tc->_kind) { + case tk_null: + case tk_void: + // nothing to encode! + break; + + case tk_char: + case tk_octet: + continue_encoding = stream->put_char (*(char *)data); + break; + + case tk_short: + case tk_ushort: + continue_encoding = stream->put_short (*(short *)data); + break; + + case tk_long: + case tk_ulong: + case tk_float: + continue_encoding = stream->put_long (*(CORBA_Long *)data); + break; + + case tk_double: + case tk_longlong: + case tk_ulonglong: + continue_encoding = stream->put_longlong (*(CORBA_LongLong *)data); + break; + + case tk_boolean: + continue_encoding = stream->put_boolean (*(CORBA_Boolean *)data); + break; + + case tk_enum: + { + // + // NOTE assumption that this is in-range. + // + // XXX should check this, it's a hard-to-recover error + // for the other side + // + unsigned value = *(unsigned *)data; + continue_encoding = stream->put_ulong (value); + } + break; + + case tk_any: + { + CORBA_Any *any = (CORBA_Any *)data; + + tc = any->type (); + if (encoder (_tc_CORBA_TypeCode, &tc, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return CORBA_TypeCode::TRAVERSE_STOP; + + data = any->value (); + return encoder (tc, data, 0, context, env); + } + // NOTREACHED + + case tk_TypeCode: + { + CORBA_TypeCode_ptr tc2; + + tc2 = *(CORBA_TypeCode_ptr *)data; + + continue_encoding = stream->put_ulong ((CORBA_ULong) tc2->_kind); + if (continue_encoding == CORBA_B_FALSE) + break; + + switch (tc2->_kind) { + // + // Most TypeCodes have empty parameter lists + // + default: + break; + + // + // A few have "simple" parameter lists + // + case tk_string: + case tk_wstring: + continue_encoding = stream->put_ulong (tc2->_length); + break; + + // + // Indirected typecodes can't occur at "top level" like + // this, only nested inside others! + // + case ~0: + dmsg ("indirected typecode at top level!"); + continue_encoding = CORBA_B_FALSE; + break; + + // + // The rest have "complex" parameter lists that are already + // encoded as bulk octets ... put length, then octets + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_sequence: + case tk_array: + case tk_alias: + case tk_except: + { + unsigned i; + + continue_encoding = stream->put_ulong (tc2->_length); + for (i = 0; i < tc2->_length && continue_encoding; i++) + continue_encoding = + stream->put_octet (tc2->_buffer [i]); + } + } + } + break; + + case tk_Principal: + { + CORBA_Principal_ptr p = *(CORBA_Principal_ptr*) data; + unsigned i; + + if (p != 0) { + continue_encoding = stream->put_long (p->id.length); + for (i = 0; continue_encoding && i < p->id.length; i++) + continue_encoding = stream->put_octet (p->id.buffer [i]); + } else + continue_encoding = stream->put_long (0); + } + break; + + case tk_objref: + // XXX implement me + break; + + case tk_sequence: + { + // + // First marshal the sequence length, verifying that + // it's within the sequence bounds ... + // + CORBA_OctetSeq *seq = (CORBA_OctetSeq *) data; + CORBA_ULong len = seq ? seq->length : 0; + + if (len > 0) { + CORBA_ULong bounds; + + bounds = tc->ulong_param (1, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + if (bounds != 0 && len > bounds) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_MAYBE)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + } + continue_encoding = stream->put_ulong (len); + + // + // Fast exit on error or empty sequence + // + if (!continue_encoding || len == 0) + break; + } + // FALLTHROUGH + + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + // + // Marshal each member in order. + // + return tc->traverse (data, 0, encoder, context, env); + + case tk_except: + // + // Convert the the "hidden" TypeCode at the beginning of the + // exception into an on-the-wire ID, then marshal the members + // in order (traversal skips that hidden typecode, and more). + // + // NOTE: This is asymmetric with respect to decoding the exception, + // since whoever decodes must pull off the ID and map it to the + // typecode to be used to unmarshal it (search among legal choices). + // + { + CORBA_String id = tc->id (env); + + if (env.exception () == 0) { + continue_encoding = + encoder (_tc_CORBA_String, &id, 0, context, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE + && tc->traverse (data, 0, encoder, context, env); + } else + continue_encoding = CORBA_B_FALSE; + } + break; + + case tk_string: + { + CORBA_String str = *(CORBA_String *) data; + CORBA_ULong len, bounds; + + // + // Be nice to programmers: treat nulls as empty strings + // not errors. (OMG-IDL supports languages that don't use + // the C/C++ notion of null v. empty strings; nulls aren't + // part of the OMG-IDL string model.) + // + if (str == 0) { + stream->put_ulong (1); + stream->put_char (0); + break; + } + + // + // Verify string satisfies bounds requirements. We're not + // so permissive as to send messages violating the interface + // spec by having excessively long strings! + // + bounds = tc->ulong_param (0, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + len = strlen ((char *)str); + + if (bounds != 0 && len > bounds) { + continue_encoding = CORBA_B_FALSE; + break; + } + + // + // Encode the string, followed by a NUL character. + // + continue_encoding = stream->put_ulong (len + 1); + while (continue_encoding != CORBA_B_FALSE && *str) + continue_encoding = stream->put_char (*str++); + stream->put_char (0); + } + break; + + case tk_wstring: + { + wchar_t *str = *(wchar_t **) data; + CORBA_ULong len, bounds; + + // + // Be nice to programmers: treat nulls as empty strings + // not errors. (OMG-IDL supports languages that don't use + // the C/C++ notion of null v. empty strings; nulls aren't + // part of the OMG-IDL string model.) + // + if (str == 0) { + stream->put_ulong (1); + stream->put_wchar (0); + break; + } + + // + // Verify wide string satisfies bounds requirements. We're + // not so permissive as to send messages violating the interface + // spec by having excessively long strings! + // + bounds = tc->ulong_param (0, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + len = wslen (str); + if (bounds != 0 && len > bounds) { + continue_encoding = CORBA_B_FALSE; + break; + } + + // + // Encode the wide string, followed by a NUL character. + // + continue_encoding = stream->put_ulong (wslen (str) + 1); + while (continue_encoding != CORBA_B_FALSE && *str) + continue_encoding = stream->put_wchar (*str++); + stream->put_wchar (0); + } + break; + + case tk_longdouble: + continue_encoding = + stream->put_longdouble (*(CORBA_LongDouble *)data); + break; + + case tk_wchar: + continue_encoding = stream->put_wchar (*(wchar_t *)data); + break; + + // case ~0: + default: + dmsg ("encoder default case ?"); + continue_encoding = CORBA_B_FALSE; + break; + } + + if (continue_encoding == CORBA_B_FALSE) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); + dmsg ("marshaling encoder detected error"); + return CORBA_TypeCode::TRAVERSE_STOP; + } + return CORBA_TypeCode::TRAVERSE_CONTINUE; +} + + + +// +// Array of typecodes used to unmarshal ... +// +extern CORBA_TypeCode_ptr __tc_consts [TC_KIND_COUNT]; + + +CORBA_TypeCode::traverse_status +XDR_stream::decoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env +) +THROWS_NOTHING +{ + CORBA_Boolean continue_decoding = CORBA_B_TRUE; + XDR_stream *stream = (XDR_stream *)context; + + switch (tc->_kind) { + case tk_null: + case tk_void: + // nothing to decode! + break; + + case tk_char: + case tk_octet: + continue_decoding = stream->get_char (*(CORBA_Char *)data); + break; + + case tk_short: + case tk_ushort: + continue_decoding = stream->get_short (*(short *)data); + break; + + case tk_long: + case tk_ulong: + case tk_float: + continue_decoding = stream->get_long (*(CORBA_Long *)data); + break; + + case tk_longlong: + case tk_ulonglong: + case tk_double: + continue_decoding = stream->get_longlong (*(CORBA_LongLong *)data); + break; + + case tk_boolean: + continue_decoding = stream->get_boolean (*(CORBA_Boolean *)data); + break; + + case tk_enum: + { + CORBA_ULong val; + + // + // NOTE assumption that this is in-range. + // + // XXX should check this, it's rather hard to recover + // from such errors since they "do not occur" and are + // essentially never tested for. + // + continue_decoding = stream->get_ulong (val); + *(unsigned *)data = (unsigned) val; + } + break; + + case tk_any: + { + CORBA_Any *any = (CORBA_Any *)data; + CORBA_TypeCode_ptr tc2; + void *value; + + if (decoder (_tc_CORBA_TypeCode, &tc2, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return CORBA_TypeCode::TRAVERSE_STOP; + + value = new CORBA_Octet [tc2->size (env)]; + + if (decoder (tc2, value, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + delete value; + CORBA_release (tc2); + return CORBA_TypeCode::TRAVERSE_STOP; + } + (void) new (any) CORBA_Any (tc2, value, CORBA_B_TRUE); + } + break; + + case tk_TypeCode: + { + CORBA_ULong kind; + CORBA_TypeCode_ptr *tcp; + + continue_decoding = stream->get_ulong (kind); + if (continue_decoding == CORBA_B_FALSE) + break; + if (kind >= TC_KIND_COUNT) { + continue_decoding = CORBA_B_FALSE; + break; + } + + tcp = (CORBA_TypeCode_ptr *)data; + + // + // Typecodes with empty parameter lists all have preallocated + // constants. We use those to reduce memory consumption and + // heap access ... also, to speed things up! + // + if (((*tcp) = __tc_consts [(unsigned) kind]) != 0) { + *tcp = __tc_consts [(unsigned) kind]; + break; + } else switch (kind) { + // + // Need special handling for all kinds of typecodes that have + // nonempty parameter lists ... + // + default: // error: missed a case! + env.exception (new CORBA_INTERNAL (COMPLETED_MAYBE)); + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Some have "simple" parameter lists ... some of these also + // have preallocated constants that could be used. + // + case tk_string: + case tk_wstring: + { + CORBA_ULong bound; + + continue_decoding = stream->get_ulong (bound); + if (continue_decoding) { + if (bound == 0) { + if (kind == tk_string) + *tcp = _tc_CORBA_String; + else + *tcp = _tc_CORBA_WString; + } else { + *tcp = new CORBA_TypeCode ((CORBA_TCKind) kind, + bound, 0, CORBA_B_TRUE); + } + } + } + break; + + // + // Indirected typecodes, illegal at "top level" but we allow + // unmarshaling of them here because we use the same code to + // read "off the wire" (where they're illegal) and to read + // out of an encapsulation stream. We distinguish the case + // where this is legal as described above. + // + case ~0: + // XXX implement me + break; + + // + // The rest have "complex" parameter lists that are + // encoded as bulk octets ... + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_sequence: + case tk_array: + case tk_alias: + case tk_except: + { + unsigned len, i; + CORBA_ULong length; + CORBA_Octet *buffer; + + continue_decoding = stream->get_ulong (length); + if (!continue_decoding) + break; + + // if length > MAXUNSIGNED, error ... + len = (unsigned) length; + + buffer = new CORBA_Octet [len]; + + for (i = 0; i < len && continue_decoding; i++) + continue_decoding = stream->get_octet (buffer [i]); + + if (!continue_decoding) { + delete buffer; + break; + } + *tcp = new CORBA_TypeCode ((CORBA_TCKind)kind, + len, buffer, CORBA_B_TRUE); + } + } + } + break; + + case tk_Principal: + { + CORBA_Principal_ptr *pp = (CORBA_Principal_ptr *)data; + CORBA_ULong len; + + continue_decoding = stream->get_ulong (len); + if (len == 0) + *pp = 0; + else { + *pp = new CORBA_Principal; + (*pp)->id.buffer = new CORBA_Octet [(size_t) len]; + (*pp)->id.maximum = (*pp)->id.length = len; + + for (unsigned i = 0; + continue_decoding != CORBA_B_FALSE && i < len; + i++) + continue_decoding = stream->get_octet ( + (*pp)->id.buffer [i]); + } + } + break; + + case tk_objref: + // XXX implement me + break; + + case tk_sequence: + { + // + // First unmarshal the sequence length ... we trust it + // to be right here, on the "be gracious in what you + // accept" principle. We don't generate illegal sequences + // (i.e. length > bounds). + // + CORBA_OctetSeq *seq = (CORBA_OctetSeq *) data; + + continue_decoding = stream->get_ulong (seq->length); + seq->maximum = seq->length; + seq->buffer = 0; + + // + // Fast exit on empty sequences or errors + // + if (!continue_decoding || seq->length == 0) + break; + + // + // ... then allocate the memory into which we'll unmarshal + // + CORBA_TypeCode_ptr tc2; + size_t size; + + tc2 = tc->typecode_param (0, env); + if (env.exception ()) + return CORBA_TypeCode::TRAVERSE_STOP; + + size = tc2->size (env); + if (env.exception ()) + return CORBA_TypeCode::TRAVERSE_STOP; + + tc2->Release (); + + seq->buffer = new CORBA_Octet [size * (size_t) seq->maximum]; + } + // FALLTHROUGH + + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + // + // Unmarshal all the individual elements using the per-member + // description held in the "parent" TypeCode. + // + + // FALLTHROUGH + + case tk_except: + // + // For exceptions, the "hidden" type ID near the front of the + // on-wire representation was previously unmarshaled and mapped + // to the "tc" typcode we're using to traverse the memory ... + // at the same time its vtable, refcount, and other state was + // established. + // + // NOTE: This is asymmetric with respect to encoding exceptions. + // + return tc->traverse (data, 0, decoder, context, env); + + case tk_string: + { + CORBA_String str; + CORBA_ULong len = 0; + + // + // On decode, omit the check against specified string bounds, + // and cope with illegal "zero length" strings (all lengths + // on the wire must include a NUL). + // + // This is on the principle of being gracious in what we accept; + // we don't generate messages that fail to comply with protocol + // specs, but we will accept them when it's clear how to do so. + // + continue_decoding = stream->get_ulong (len); + *((CORBA_String*)data) = str = new CORBA_Char [(size_t) (len)]; + if (len != 0) + while (continue_decoding != CORBA_B_FALSE && len-- != 0) { + continue_decoding = stream->get_char (*(CORBA_Char *)str); + str++; + } + break; + } + + case tk_wstring: + { + wchar_t *str; + CORBA_ULong len = 0; + + // + // On decode, omit the check against specified wstring bounds, + // and cope with illegal "zero length" strings (all lengths + // on the wire must include a NUL). + // + // This is on the principle of being gracious in what we accept; + // we don't generate messages that fail to comply with protocol + // specs, but we will accept them when it's clear how to do so. + // + continue_decoding = stream->get_ulong (len); + *((wchar_t **)data) = str = new wchar_t [(size_t) (len)]; + if (len != 0) { + while (continue_decoding != CORBA_B_FALSE && len--) { + continue_decoding = stream->get_wchar (*str); + str++; + } + } + } + break; + + case tk_longdouble: + continue_decoding = + stream->get_longdouble (*(CORBA_LongDouble *)data); + break; + + case tk_wchar: + continue_decoding = stream->get_wchar (*(wchar_t *)data); + break; + + // case ~0: + default: + continue_decoding = CORBA_B_FALSE; + dmsg ("decode, default case?"); + break; + } + + if (continue_decoding == CORBA_B_FALSE) { + env.exception (new CORBA_MARSHAL (COMPLETED_NO)); + dmsg ("marshaling decoder detected error"); + return CORBA_TypeCode::TRAVERSE_STOP; + } + return CORBA_TypeCode::TRAVERSE_CONTINUE; +} + + +// +// Write an XDR message fragment out on the stream. +// +CORBA_Boolean +XDR_stream::flush_frag (CORBA_Boolean is_last) +THROWS_NOTHING +{ + return CORBA_B_FALSE; + +#if 0 + int status; + CORBA_ULong size, header; + + size = index * sizeof (CORBA_ULong); // byte length of msg + if (is_last) + header = 0x80000000 | size; // indicates last frag + else + header = size; + buffer [0] = htonl (header); + + // + // XXX for portability, loop until there's no error. Some + // platforms/mode don't guarantee full TCP writes even when + // async (or nonblocking) mode was not set on this socket. + // + size += sizeof (CORBA_ULong); + status = write (fd, &buffer [0], size); + + index = 0; + + return (status == size); +#endif +} + + +// +// Read an XDR message fragment in from the stream. +// +CORBA_Boolean +XDR_stream::read_frag () +THROWS_NOTHING +{ + // read cookie, split out size and "is last" flag + // read rest of buffer + + return CORBA_B_FALSE; +} diff --git a/TAO/IIOP/lib/onc/xdr.hh b/TAO/IIOP/lib/onc/xdr.hh new file mode 100644 index 00000000000..e50544662bf --- /dev/null +++ b/TAO/IIOP/lib/onc/xdr.hh @@ -0,0 +1,281 @@ +// @(#)xdr.hh 1.3 95/11/07 +// Copyright 1995 by Sun Microsystems, Inc. +// +// XDR stream interface and implementation (partially implemented) +// +// XXX as of 3-Nov-95 XDR_stream should only be relied on to marshal the +// simplest primitives ... not objrefs, typecodes, etc. Also, the +// handling of sequences of chars/octets/shorts/wchars is wrong. +// + +#ifndef _xdr_hh +#define _xdr_hh + +#include <sys/types.h> +#include <netinet/in.h> + +// +// Define on systems that fully support exceptions ... excluding +// some very common platforms like G++ and VC++ 2.2 +// +// #define THROWS_NOTHING throw() + +// +// On DevPro's 4.0.1a compilers, null throw specs carry a big +// performance penalty: slower by about 25% here. Disable!! +// +#define THROWS_NOTHING + +class XDR_stream +{ + public: + enum { + STANDARD_BUFSIZE = 4096, // ~3 enet packets with TCP + BUFFER_LEN = 1 + (STANDARD_BUFSIZE + / sizeof (CORBA_Long)) + }; + + XDR_stream ( + int _fd + ) THROWS_NOTHING : + fd (_fd) + { + index = 0; + decode_flag = CORBA_B_FALSE; + max_index = 0; + is_last_frag = CORBA_B_FALSE; + } + + ~XDR_stream () THROWS_NOTHING {} + + + // + // Write the last bit of an encoded message ... or tell if we've + // decoded all the data in an encoded message we're processing. + // + // NOTE: these assume the mode is appropriate for these operations. + // + CORBA_Boolean output_msg_at_end () THROWS_NOTHING + { return flush_frag (CORBA_B_TRUE); } + CORBA_Boolean input_msg_at_end () THROWS_NOTHING + { return is_last_frag && index == max_index; } + + // + // Tell if we're decoding or encoding data. + // + CORBA_Boolean is_decoding () THROWS_NOTHING + { return decode_flag; } + CORBA_Boolean is_encoding () THROWS_NOTHING + { return !is_decoding (); } + + // private ... iff there's a routine to set it! + CORBA_Boolean decode_flag; // true iff decoding + + // + // ENCODING SUPPORT ... + // + CORBA_Boolean put32 (CORBA_Long word) THROWS_NOTHING + { + if (index < (BUFFER_LEN - 1) + || (flush_frag (CORBA_B_FALSE) + == CORBA_B_TRUE)) { + buffer [++index] = htonl (word); + return CORBA_B_TRUE; + } else + return CORBA_B_FALSE; + } + + CORBA_Boolean put_byte (char c) THROWS_NOTHING + { return put32 (c); } + CORBA_Boolean put_short (CORBA_Short s) THROWS_NOTHING + { return put32 (s); } + CORBA_Boolean put_long (CORBA_Long l) THROWS_NOTHING + { return put32 (l); } + + CORBA_Boolean put_longlong (const CORBA_LongLong &ll) + THROWS_NOTHING; + + inline CORBA_Boolean put_char (CORBA_Char c) THROWS_NOTHING + { return put_byte ((char) c); } + inline CORBA_Boolean put_wchar (wchar_t wc) THROWS_NOTHING + { + // + // "wchar_t" isn't always 2 bytes, such + // systems might need further conversion + // (e.g. hosts with multibyte characters + // native, rather than UNICODE) + // + return put_short ((short)wc); + } + + inline CORBA_Boolean put_boolean (CORBA_Boolean b) THROWS_NOTHING + { return put_byte ((char) + (b != CORBA_B_FALSE)); } + + inline CORBA_Boolean put_octet (CORBA_Octet o) THROWS_NOTHING + { return put_byte ((char) o); } + inline CORBA_Boolean put_ushort (CORBA_UShort s) THROWS_NOTHING + { return put_short ((CORBA_Short) s); } + inline CORBA_Boolean put_ulong (CORBA_ULong l) THROWS_NOTHING + { return put_long ((CORBA_Long) l); } + inline CORBA_Boolean put_ulonglong (const CORBA_ULongLong &ll) + THROWS_NOTHING + { return + put_longlong ((CORBA_LongLong &) ll); } + + inline CORBA_Boolean put_float (float f) THROWS_NOTHING + { return put_long (*(CORBA_Long *) &f); } + inline CORBA_Boolean put_double (const double &d) THROWS_NOTHING + { return + put_longlong (*(CORBA_LongLong *) &d); } + + CORBA_Boolean put_longdouble (const CORBA_LongDouble &ld) + THROWS_NOTHING; + + // + // marshaling interpreter ... 'context' really points to a stream. + // + static CORBA_TypeCode::traverse_status + encoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env + ) THROWS_NOTHING; + + // + // DECODING SUPPORT + // + // XXX fix lack of error reporting here !! + // + + CORBA_Long get32 () THROWS_NOTHING + { + if (index <= max_index + || read_frag () == CORBA_B_TRUE) + return ntohl (buffer [index++]); + else + return 0; + } + + + CORBA_Boolean get_byte (char &c) THROWS_NOTHING + { + c = (char) get32(); + return CORBA_B_TRUE; + } + + CORBA_Boolean get_short (CORBA_Short &s) THROWS_NOTHING + { + s = (short) get32(); + return CORBA_B_TRUE; + } + + CORBA_Boolean get_long (CORBA_Long &l) THROWS_NOTHING + { + l = get32(); + return CORBA_B_TRUE; + } + + CORBA_Boolean get_longlong (CORBA_LongLong &ll) + THROWS_NOTHING; + + inline CORBA_Boolean get_char (CORBA_Char &o) THROWS_NOTHING + { return get_byte ((char &) o); } + inline CORBA_Boolean get_wchar (wchar_t &wc) THROWS_NOTHING + { + short s; + + // + // wchar_t isn't always "short" + // + CORBA_Boolean retval = get_short (s); + wc = s; + return retval; + } + + inline CORBA_Boolean get_boolean (CORBA_Boolean &b) THROWS_NOTHING + { + CORBA_Char c; + + // + // CORBA_Boolean is rarely 'char' + // + CORBA_Boolean retval = get_char (c); + b = (c == 1); + return retval; + } + + inline CORBA_Boolean get_octet (CORBA_Octet &o) THROWS_NOTHING + { return get_byte ((char &) o); } + inline CORBA_Boolean get_ushort (CORBA_UShort &s) THROWS_NOTHING + { return get_short ((short&) s); } + inline CORBA_Boolean get_ulong (CORBA_ULong &l) THROWS_NOTHING + { return get_long ((CORBA_Long &) l); } + inline CORBA_Boolean get_ulonglong (const CORBA_ULongLong &ull) + THROWS_NOTHING + { return + get_longlong ((CORBA_LongLong &) ull); } + + inline CORBA_Boolean get_float (float &f) THROWS_NOTHING + { return get_long ((CORBA_Long &) f); } + inline CORBA_Boolean get_double (double &d) THROWS_NOTHING + { return + get_longlong ((CORBA_LongLong &) d); } + + CORBA_Boolean get_longdouble (CORBA_LongDouble &ld) + THROWS_NOTHING; + + // + // unmarshaling interpreter ... 'context' really points to a buffer. + // + static CORBA_TypeCode::traverse_status + decoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env + ) THROWS_NOTHING; + + private: + // + // Low level I/O primitives ... flush a fragment (maybe as the last one), + // read a fragment in. + // + CORBA_Boolean flush_frag (CORBA_Boolean is_last) + THROWS_NOTHING; + CORBA_Boolean read_frag () THROWS_NOTHING; + + // + // The actual buffer and the index to the current entry. + // (Next buffer entry read/written is index+1 ...) + // + CORBA_Long buffer [BUFFER_LEN]; + unsigned index; + + // + // The (TCP) stream on which this writes its message fragments. + // + const int fd; + + // + // Used when reading fragments ... max_index controls where the + // end of the fragment is recorded to be, and is_last_frag says + // if it's OK to read_frag() to get the next fragment. + // + // To move to the next message in the stream, create a new XDR + // stream (e.g. on the stack). + // + CORBA_Long max_index; + CORBA_Boolean is_last_frag; + + // + // Two operations not supported by this class. + // + XDR_stream (const XDR_stream &); + XDR_stream &operator = (const XDR_stream &); +}; + +#endif // _xdr_hh diff --git a/TAO/IIOP/lib/runtime/Makefile b/TAO/IIOP/lib/runtime/Makefile new file mode 100644 index 00000000000..e570c2c2341 --- /dev/null +++ b/TAO/IIOP/lib/runtime/Makefile @@ -0,0 +1,13 @@ +FILES = \ + any.cpp cdr.cpp corbacom.cpp debug.cpp except.cpp interp.cpp \ + marshal.cpp object.cpp orbobj.cpp principa.cpp tc_const.cpp \ + typecode.cpp nvlist.cpp request.cpp toa.cpp + +ROOT = ../.. + +include ../Makefile.gen + +t-xdr: t-xdr.cpp + $(LINK.cc) -o t-xdr t-xdr.cpp \ + -qoption ld -R.. -L.. -lcorba \ + -lsocket -lnsl diff --git a/TAO/IIOP/lib/runtime/align.hh b/TAO/IIOP/lib/runtime/align.hh new file mode 100644 index 00000000000..6b9df81b9ff --- /dev/null +++ b/TAO/IIOP/lib/runtime/align.hh @@ -0,0 +1,63 @@ +// @(#)align.hh 1.1 95/11/04 +// Copyright 1994-1995 by Sun Microsystems, Inc. +// +// Pointer alignment utilities +// +// A "ptr_arith_t" type is defined for doing numerical operations on +// pointers, such as aligning them. Pointer sizes vary from 2 to 8 +// bytes in today's environments; a portable data type is much needed. +// + +#ifndef _align_hh +#define _align_hh + +// +// Type for doing arithmetic on pointers ... as elsewhere, we assume +// that "unsigned" versions of a type are the same size as the "signed" +// version of the same type. +// + +#if SIZEOF_VOID_P == SIZEOF_INT +typedef unsigned int ptr_arith_t; + +#elif SIZEOF_VOID_P == SIZEOF_LONG +typedef unsigned long ptr_arith_t; + +#elif SIZEOF_VOID_P == SIZEOF_LONG_LONG +typedef unsigned long long ptr_arith_t; + +#else +# error "Can't find a suitable type for doing pointer arithmetic." +#endif + + + +// +// Efficiently align "value" up to "alignment", knowing that all such +// boundaries are binary powers and that we're using two's complement +// arithmetic. +// +static inline ptr_arith_t +align_binary (const ptr_arith_t value, size_t alignment) +{ + ptr_arith_t temp = alignment - 1; + + return (value + temp) & ~temp; +} + + +// +// Efficiently round "ptr" up to an "alignment" boundary, knowing that +// all such boundaries are binary powers and that we're using two's +// complement arithmetic. +// +// XXX Returned as "byte pointer" -- CDR module would change to be seen +// as a "void *". May want to change this to add XDR cleanly. +// +static inline unsigned char * +ptr_align_binary (const unsigned char *ptr, size_t alignment) +{ + return (unsigned char *) align_binary ((ptr_arith_t) ptr, alignment); +} + +#endif // _align_hh diff --git a/TAO/IIOP/lib/runtime/any.cpp b/TAO/IIOP/lib/runtime/any.cpp new file mode 100644 index 00000000000..d5cbed7b512 --- /dev/null +++ b/TAO/IIOP/lib/runtime/any.cpp @@ -0,0 +1,670 @@ +// @(#)any.cpp 1.9 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: Implementation of CORBA_Any +// +// This includes three constructors, a destructor, and a "replace" method +// for the "Any" data type. "Any" values pair a pointer to a data structure +// in the native binary representation (e.g. C struct) with a TypeCode that +// describes that data structure. +// +// The copy constructor and the destructor each use the TypeCode interpreter +// with specialized "visit" callback routines. The "visit" routines are +// used respectively to make "deep copies" and perform "deep frees" of the +// aritrary values as described by the "Any" value's typecode. +// +// Note that these "visit" routines are called directly, and they choose +// whether or not to use the TypeCode interpreter to examine constituents. +// In the simple cases, the "visit" routines can do their work without any +// further calls; only for constructed types is the interpreter's knowledge +// really required. +// +// THREADING NOTE: "Any" is a data structure which must be protected by +// external critical sections. Like simpler numeric types, "Any" instances +// are accessed and modified atomically. This implementation is reentrant, +// so that independent "Any" values may be manipulated concurrently when +// the underlying programming environment is itself reentrant. +// +// COM NOTE: Yes, this is a utility data type whose implementation is +// fully exposed. Factories for these are not normally used in C++. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/debug.hh" +#include "runtime/thread.hh" + +#include <initguid.h> + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t any_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + + +CORBA_TypeCode_ptr +CORBA_Any::type () const +{ + return _type; +} + +void * +CORBA_Any::value () const +{ + return _value; +} + +// +// Default "Any" constructor -- initializes to nulls per the +// OMG C++ mapping. +// +// NOTE: null (zero) typecode pointers are also treated as +// the null typecode ... +// +CORBA_Any::CORBA_Any () +{ + _type = _tc_CORBA_Null; + _value = 0; + _orb_owns_data = CORBA_B_FALSE; + _refcnt = 1; +} + + +// +// The more common "Any" constructor has its own copy of a +// typecode, and either holds or "consumes" an arbitrary data +// value satisfying the normal binary interface rules. +// +CORBA_Any::CORBA_Any ( + CORBA_TypeCode_ptr tc, + void *value, + CORBA_Boolean orb_owns_data +) : + _value (value), + _orb_owns_data (orb_owns_data) +{ + _type = tc; + tc->AddRef (); + _refcnt = 1; +} + + +// +// Helper routine for "Any" copy constructor ... +// +// "Deep Copy" from source to dest. Memory is always there to be +// copied to ... if this calls itself recursively, it ensures that +// this remains true (only really an issue for sequences). +// +// This shows the main reason to pass two values to the "visit" +// function used by the TypeCode interpreter: it allows the copy +// to be made without using any additional temporary memory. Most +// other such "visit" routines use only a single value. This is +// also slightly atypical in that it doesn't use the "context". +// +static CORBA_TypeCode::traverse_status +deep_copy ( + CORBA_TypeCode_ptr tc, + const void *source, + const void *dest, + void *, // no context + CORBA_Environment &env +) +{ + CORBA_TypeCode::traverse_status retval; + CORBA_TCKind my_kind; + + if (!tc) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + my_kind = tc->kind (env); + + if (env.exception_type () != NO_EXCEPTION) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Deep copy from "source" to "dest" ... this code "knows" a bit about + // representations, verify it when porting to oddball platforms with + // non-IEEE floating point values or atypical byte and word sizes. + // + // See the TypeCode interpreter code for more details about the + // representational assumptions here. + // + retval = CORBA_TypeCode::TRAVERSE_CONTINUE; + + switch (my_kind) { + case tk_null: + case tk_void: + break; + + case tk_char: + case tk_octet: + *(CORBA_Octet *)dest = *(CORBA_Octet *)source; + break; + + case tk_short: + case tk_ushort: + *(CORBA_Short *)dest = *(CORBA_Short *)source; + break; + + case tk_wchar: + *(CORBA_WChar *)dest = *(CORBA_WChar *)source; + break; + + case tk_long: + case tk_ulong: + case tk_float: + *(CORBA_Long *)dest = *(CORBA_Long *)source; + break; + + case tk_longlong: + case tk_ulonglong: + case tk_double: + *(CORBA_LongLong *)dest = *(CORBA_LongLong *)source; + break; + + case tk_longdouble: + *(CORBA_LongDouble *)dest = *(CORBA_LongDouble *)source; + break; + + case tk_boolean: + *(CORBA_Boolean *)dest = *(CORBA_Boolean *)source; + break; + + case tk_any: + (void) new (dest) CORBA_Any (*(CORBA_Any*)source); + break; + + case tk_TypeCode: + if ((*(CORBA_TypeCode_ptr *)source) != 0) + dest = source; + else + dest = _tc_CORBA_Null; + ((CORBA_TypeCode_ptr)dest)->AddRef (); + break; + + case tk_Principal: + { + CORBA_Principal_ptr src, dst; + + src = *(CORBA_Principal_ptr *)source; + dst = *(CORBA_Principal_ptr *)dest = new CORBA_Principal; + + // + // Principals are just opaque IDs ... copy them + // + assert (src->id.length <= UINT_MAX); + dst->id.length = dst->id.maximum = src->id.length; + if (dst->id.length > 0) { + dst->id.buffer = new CORBA_Octet [(unsigned) dst->id.length]; + memcpy (dst->id.buffer, src->id.buffer, + (size_t) dst->id.length); + } else { + dst->id.buffer = 0; + } + } + break; + + case tk_objref: + *(CORBA_Object_ptr *)dest = CORBA_Object:: + _duplicate (*(CORBA_Object_ptr *) source); + break; + + case tk_sequence: + { + CORBA_OctetSeq *src, *dst; + CORBA_TypeCode_ptr tcp; + size_t size; + + // + // Rely on binary format of sequences -- all are the + // same except for the type pointed to by "buffer" + // + src = (CORBA_OctetSeq *)source; + dst = (CORBA_OctetSeq *)dest; + + assert (src->length <= UINT_MAX); + dst->length = dst->maximum = src->length; + + // + // Get the size of each "buffer" element + // + tcp = tc->typecode_param (0, env); + if (env.exception () != 0) { + retval = CORBA_TypeCode::TRAVERSE_STOP; + break; + } + + size = tcp->size (env); + if (env.exception () != 0) { + retval = CORBA_TypeCode::TRAVERSE_STOP; + break; + } + tcp->Release (); + + // + // Now allocate a new (uninitialized) buffer of the right + // size to hold that many elements ... fall through and + // let a general traverse fill in those buffer elements. + // + size *= (size_t) src->length; + dst->buffer = new CORBA_Octet [size]; + } + // FALLTHROUGH + + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + return tc->traverse (source, dest, + (CORBA_TypeCode::VisitRoutine) deep_copy, 0, env); + + case tk_except: + // + // Exceptions in memory have a "hidden" typecode up front, + // used to ensure that memory is appropriately freed and + // to hold the exception ID. We just copy that typecode, + // the traverse code ignores it completely. + // + *(CORBA_TypeCode_ptr *) dest = *(CORBA_TypeCode_ptr *) source; + (void) (*(CORBA_TypeCode_ptr *) dest)->AddRef (); + return tc->traverse (source, dest, + (CORBA_TypeCode::VisitRoutine) deep_copy, 0, env); + + case tk_enum: + *(int *)dest = *(int *)source; + break; + + case tk_string: + *(CORBA_String *)dest = + CORBA_string_copy (*(CORBA_String *)source); + break; + + case tk_wstring: + *(CORBA_WString *)dest = + CORBA_wstring_copy (*(CORBA_WString *)source); + break; + + default: + dmsg ("deep copy default case ?"); + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + retval = CORBA_TypeCode::TRAVERSE_STOP; + break; + } + return retval; +} + + +// +// Copy constructor for "Any". +// +CORBA_Any::CORBA_Any (const CORBA_Any &src) +{ + CORBA_Environment env; + size_t size; + + if (src._type != 0) + _type = src._type; + else + _type = _tc_CORBA_Null; + + _type->AddRef (); + _orb_owns_data = CORBA_B_TRUE; + + size = _type->size (env); // XXX check error status + _value = (char *) calloc (1, size); + + (void) _type->traverse (src._value, _value, + (CORBA_TypeCode::VisitRoutine) deep_copy, 0, env); +} + + +// +// Helper routine for "Any" destructor. +// +// This frees all the memory pointed to by any given value held inside +// of an "Any". For most data types it does nothing, since most data +// types don't hold any memory. For a few, it recurses. +// +// This is one of the simplest typecode interpreter callbacks, since +// in most cases it does nothing. Also, it uses neither the second +// value nor the context parameter. +// +static CORBA_TypeCode::traverse_status +deep_free ( + CORBA_TypeCode_ptr tc, + const void *value, + const void *, // value2 unused + void *, // context unused + CORBA_Environment &env +) +{ + // + // don't do anything if the value is a null pointer + // + if (!value) + return CORBA_TypeCode::TRAVERSE_CONTINUE; + + CORBA_TypeCode::traverse_status retval; + CORBA_TCKind my_kind; + + if (!tc) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + my_kind = tc->kind (env); + + if (env.exception_type () != NO_EXCEPTION) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Free only embedded pointers ... which don't exist in most + // primitive types. + // + retval = CORBA_TypeCode::TRAVERSE_CONTINUE; + switch (my_kind) { + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + return tc->traverse (value, 0, + (CORBA_TypeCode::VisitRoutine) deep_free, 0, env); + + // + // XXX: Exceptions are currently leaked because of bugs + // lurking in this area. Keep in mind that there are + // two things to free: (a) the typecode in the exception + // base class; (b) any pointers held by a user-defined + // exception, such as an objref or string. + // + // Since this code does nothing, it should leak BOTH of those + // kinds of memory. Since it's not supposed to be called + // except when the exception really is being freed, it should + // only be called when the reference count in the exception + // base class is zero. + // + // It's not clear which of those assertions actually hold. + // + // The code SHOULD be just like the traverse() call for a + // structure, with (a) a precondition that the reference + // count is zero, (b) an assertion that the typecode in the + // exception and "tc" are equivalent, (c) releasing that + // typecode found within the exception. + // + case tk_except: + return retval; + + + case tk_sequence: + retval = tc->traverse (value, 0, + (CORBA_TypeCode::VisitRoutine) deep_free, 0, env); + delete ((CORBA_OctetSeq *)value)->buffer; + break; + + case tk_TypeCode: + if ((*(CORBA_TypeCode_ptr *)value) != 0) + (*(CORBA_TypeCode_ptr *)value)->Release (); + break; + + case tk_Principal: + CORBA_release (*(CORBA_Principal_ptr *)value); + break; + + case tk_objref: + CORBA_release (*(CORBA_Object_ptr *)value); + break; + + case tk_string: + CORBA_string_free (*(CORBA_String *)value); + break; + + case tk_wstring: + CORBA_wstring_free (*(CORBA_WString *)value); + break; + + case tk_any: +#ifdef __BORLANDC__ + // + // XXX BC++ doesn't yet accept explicit calls to destructors + // with this syntax. A simple workaround must exist, though; + // other explicit destructor calls work. + // + dmsg ("Delete Any-in-Any ... memory leak with BC++ 4.5"); +#else + ((CORBA_Any *)value)->~CORBA_Any (); +#endif + break; + + default: + return CORBA_TypeCode::TRAVERSE_CONTINUE; + } + + if (env.exception_type () != NO_EXCEPTION) + return CORBA_TypeCode::TRAVERSE_STOP; + else + return retval; +} + + +// +// Destructor for an "Any" deep-frees memory if needed. +// +// NOTE that the assertion below will fire on application programmer errors, +// such as using AddRef/Release out of sync with the true lifetime of an Any +// value allocated on the stack. BUT it involves changing the refcounting +// policy so that it's initialized to zero, not one ... which policy affects +// the whole source base, and not just this data type. Get to this later. +// +CORBA_Any::~CORBA_Any () +{ + CORBA_Environment env; + + // assert (_refcnt == 0); + + if (_orb_owns_data) { + (void) deep_free (_type, _value, 0, 0, env); + delete _value; + } + if (_type) + _type->Release (); +} + + +// +// all-at-once replacement of the contents of an "Any" +// +void +CORBA_Any::replace ( + CORBA_TypeCode_ptr tc, + const void *v, + CORBA_Boolean orb_owns_data, + CORBA_Environment &env +) +{ + if (_orb_owns_data) { + (void) deep_free (_type, _value, 0, 0, env); + delete _value; + } + + if (_type != 0) + _type->Release (); + + env.clear (); + + _type = tc; + tc->AddRef (); + _value = (void *) v; + _orb_owns_data = orb_owns_data; +} + +// +// For COM -- IUnKnown operations +// + +// {A201E4C8-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_Any, +0xa201e4c8, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_Any::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&any_lock); +#endif + + return ++_refcnt; +} + +ULONG +__stdcall +CORBA_Any::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&any_lock); +#endif + + if (--_refcnt != 0) + return _refcnt; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_Any::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_Any == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + + +// +// VARIANT conversions +// + +// copy constructor +CORBA_Any::CORBA_Any (const VARIANT &src) +{ + _orb_owns_data = CORBA_B_TRUE; + _refcnt = 1; + _type = _tc_CORBA_Void; + _value = 0; + + *this = src; +} + +// assignment operator +CORBA_Any & +CORBA_Any::operator = (const VARIANT &src) +{ + this->~CORBA_Any (); + + assert ((src.vt & 0xB000) == 0); // XXX better, report exception + + switch (src.vt & 0x0fff) { + case VT_EMPTY: + _type = _tc_CORBA_Void; + _value = 0; + break; + + case VT_NULL: + _type = _tc_CORBA_Null; + _value = 0; + break; + + case VT_I2: + _type = _tc_CORBA_Short; + _value = new CORBA_Short ((src.vt & VT_BYREF) + ? (*src.piVal) : src.iVal); + break; + + case VT_I4: + _type = _tc_CORBA_Long; + _value = new CORBA_Long ((src.vt & VT_BYREF) + ? (*src.plVal) : src.lVal); + break; + + case VT_R4: + _type = _tc_CORBA_Float; + _value = new CORBA_Float ((src.vt & VT_BYREF) + ? (*src.pfltVal) : src.fltVal); + break; + + case VT_R8: + _type = _tc_CORBA_Double; + _value = new CORBA_Double ((src.vt & VT_BYREF) + ? (*src.pdblVal) : src.dblVal); + break; + + // case VT_CY: + // case VT_DATE: + // XXX convert currency and date to TBD CORBA conventions + + // case VT_BSTR: + // XXX convert to CORBA string + + // case VT_DISPATCH: + // case VT_UNKNOWN: + // case VT_VARIANT: + // XXX convert to CORBA objref or appropriate pseudo-objref + + // case VT_BOOL: + // XXX convert to CORBA boolean + + // case VT_ERROR: + // XXX what to do? + + case VT_UI1: + _type = _tc_CORBA_Octet; + _value = new CORBA_Octet ((src.vt & VT_BYREF) + ? (*src.pbVal) : src.bVal); + break; + + default: + // XXX report some exception ... throw it? + _type = _tc_CORBA_Void; + _value = 0; + break; + } + + return *this; +} + +CORBA_Any::operator VARIANT () +{ + VARIANT retval; + + // XXX convert it ... or report exception somehow! + + retval.vt = VT_EMPTY; + return retval; +} diff --git a/TAO/IIOP/lib/runtime/cdr.cpp b/TAO/IIOP/lib/runtime/cdr.cpp new file mode 100644 index 00000000000..b9652ec14ba --- /dev/null +++ b/TAO/IIOP/lib/runtime/cdr.cpp @@ -0,0 +1,460 @@ +// @(#)cdr.cpp 1.2 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// CDR: Encode/Decode basic machine data types +// +// Implementation of OMG "Common Data Representation" (CDR) ... there are +// one routine each for byte/halfword/word/doubleword put/get, which adjust +// to establish "natural" alignment (the bulk of the code) and then put or +// get with byteswapping as needed. +// +// The implementation knows that native data formats are conformant with +// OMG-IDL's (and hence CDR's) size requirements, and relies on the fact +// that (for example) CORBA_Long is always four bytes long even if the +// environment's "int" is a different size. +// +// char, octet 8 bits (1 byte) +// short, unsigned short 16 bits (2 bytes) +// long, unsigned long, float 32 bits (4 bytes) +// double, (unsigned) long long 64 bits (8 bytes) +// long double 128 bits (16 bytes) +// +// Moreover, this "knows" that the native 'char' represents ISO Latin/1 +// characters (an ASCII superset addressing Western European characters) +// and that "double" and "float" comply with the IEEE standards. (The +// "long double" may not be a native data type, though.) +// +// THREADING NOTE: "CDR" is a data structure which must be protected by +// external critical sections. Like simpler numeric types, "CDR" instances +// are accessed and modified atomically. This implementation is reentrant, +// so that independent "CDR" values may be manipulated concurrently when +// the underlying programming environment is itself reentrant. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <corba/orb.hh> + +#include "runtime/debug.hh" +#include "runtime/cdr.hh" + + +// +// ENCODING routines ... pad, store. Never swap. Padding uses +// whatever value is already in the buffer. +// +CORBA_Boolean +CDR::put_byte ( + char c +) +{ + if (remaining < sizeof (char) && grow (0) == CORBA_B_FALSE) + return CORBA_B_FALSE; + + *next++ = (unsigned char) c; + remaining--; + return CORBA_B_TRUE; +} + + +CORBA_Boolean +CDR::put_short ( + CORBA_Short s +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes; maybe + // grow the buffer if there's not enough left + // + tmp_next = ptr_align_binary (next, SHORT_SIZE); + temp = SHORT_SIZE + (tmp_next - next); + if (temp > remaining) { + if (grow (0) == CORBA_B_FALSE) + return CORBA_B_FALSE; + tmp_next = next + temp - SHORT_SIZE; + } + remaining -= temp; + + // + // copy the half word, native byte order + // + *(CORBA_Short *)tmp_next = s; + next = tmp_next + SHORT_SIZE; + return CORBA_B_TRUE; +} + +CORBA_Boolean +CDR::put_long ( + CORBA_Long l +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes; maybe + // grow the buffer if there's not enough left + // + tmp_next = ptr_align_binary (next, LONG_SIZE); + temp = LONG_SIZE + (tmp_next - next); + if (temp > remaining) { + if (grow (0) == CORBA_B_FALSE) + return CORBA_B_FALSE; + tmp_next = next + temp - LONG_SIZE; + } + remaining -= temp; + + // + // copy the word, native byte order + // + *(CORBA_Long *)tmp_next = l; + + next = tmp_next + LONG_SIZE; + return CORBA_B_TRUE; +} + +CORBA_Boolean +CDR::put_longlong ( + const CORBA_LongLong &ll +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes; maybe + // grow the buffer if there's not enough left + // + tmp_next = ptr_align_binary (next, LONGLONG_SIZE); + temp = LONGLONG_SIZE + (tmp_next - next); + if (temp > remaining) { + if (grow (0) == CORBA_B_FALSE) + return CORBA_B_FALSE; + tmp_next = next + temp - LONGLONG_SIZE; + } + remaining -= temp; + + // + // copy the double word in "native" byte order. + // + *(CORBA_LongLong *)tmp_next = ll; + next = tmp_next + LONGLONG_SIZE; + return CORBA_B_TRUE; +} + +CORBA_Boolean +CDR::put_longdouble ( + CORBA_LongDouble &ld +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes; maybe + // grow the buffer if there's not enough left + // + tmp_next = ptr_align_binary (next, LONGDOUBLE_SIZE); + temp = LONGDOUBLE_SIZE + (tmp_next - next); + if (temp > remaining) { + if (grow (0) == CORBA_B_FALSE) + return CORBA_B_FALSE; + tmp_next = next + temp - LONGDOUBLE_SIZE; + } + remaining -= temp; + + // + // copy the long double in "native" byte order. + // + *(CORBA_LongDouble *)tmp_next = ld; + next = tmp_next + LONGDOUBLE_SIZE; + return CORBA_B_TRUE; +} + + +// +// DECODING routines ... adjust pointer, then byteswap as needed. +// + +CORBA_Boolean +CDR::get_byte ( + char &c +) +{ + if (remaining < sizeof (char)) + return CORBA_B_FALSE; + + c = (char) *next++; + remaining--; + return CORBA_B_TRUE; +} + + +CORBA_Boolean +CDR::get_short ( + CORBA_Short &s +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes + // + tmp_next = ptr_align_binary (next, SHORT_SIZE); + temp = SHORT_SIZE + (tmp_next - next); + if (temp > remaining) + return CORBA_B_FALSE; + remaining -= temp; + + // + // decode halfword, swapping as needed + // + if (!do_byteswap) { + s = *(CORBA_Short *)tmp_next; + next = tmp_next + SHORT_SIZE; + } else { + register unsigned char *sp = (unsigned char *) &s; + + sp [1] = *tmp_next++; + sp [0] = *tmp_next++; + next = tmp_next; + } + return CORBA_B_TRUE; +} + +CORBA_Boolean +CDR::get_long ( + CORBA_Long &l +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes + // + tmp_next = ptr_align_binary (next, LONG_SIZE); + temp = LONG_SIZE + (tmp_next - next); + if (temp > remaining) + return CORBA_B_FALSE; + remaining -= temp; + + // + // decode word, swapping as needed + // + if (!do_byteswap) { + l = *(CORBA_Long *)tmp_next; + next = tmp_next + LONG_SIZE; + } else { + register unsigned char *lp = (unsigned char *) &l; + + // + // NOTE: environment-specific speedups abound for this kind + // of stuff. This generic code takes advanage of none of them. + // + lp [3] = *tmp_next++; + lp [2] = *tmp_next++; + lp [1] = *tmp_next++; + lp [0] = *tmp_next++; + next = tmp_next; + } + return CORBA_B_TRUE; +} + +CORBA_Boolean +CDR::get_longlong ( + CORBA_LongLong &ll +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes + // + tmp_next = ptr_align_binary (next, LONGLONG_SIZE); + temp = LONGLONG_SIZE + (tmp_next - next); + if (temp > remaining) + return CORBA_B_FALSE; + remaining -= temp; + + // + // decode doubleword, swapping as needed + // + if (!do_byteswap) { + ll = *(CORBA_LongLong *)tmp_next; + next = tmp_next + LONGLONG_SIZE; + } else { + register unsigned char *llp = (unsigned char *) ≪ + + // + // NOTE: environment-specific speedups abound for this kind + // of stuff. This generic code takes advanage of none of them. + // + llp [7] = *tmp_next++; + llp [6] = *tmp_next++; + llp [5] = *tmp_next++; + llp [4] = *tmp_next++; + llp [3] = *tmp_next++; + llp [2] = *tmp_next++; + llp [1] = *tmp_next++; + llp [0] = *tmp_next++; + next = tmp_next; + } + return CORBA_B_TRUE; +} + + +CORBA_Boolean +CDR::get_longdouble ( + CORBA_LongDouble &ld +) +{ + register unsigned char *tmp_next; + register unsigned temp; + + // + // Adjust pointer and count of remaining bytes + // + tmp_next = ptr_align_binary (next, LONGDOUBLE_SIZE); + temp = LONGDOUBLE_SIZE + (tmp_next - next); + if (temp > remaining) + return CORBA_B_FALSE; + remaining -= temp; + + // + // copy the long double, swapping bytes as needed + // + if (!do_byteswap) { + ld = *(CORBA_LongDouble *)tmp_next; + next = tmp_next + LONGDOUBLE_SIZE; + } else { + register unsigned char *ldp = (unsigned char *) &ld; + + // + // NOTE: this is a single SPARC V9 instruction + // + ldp [15] = *tmp_next++; + ldp [14] = *tmp_next++; + ldp [13] = *tmp_next++; + ldp [12] = *tmp_next++; + ldp [11] = *tmp_next++; + ldp [10] = *tmp_next++; + ldp [ 9] = *tmp_next++; + ldp [ 8] = *tmp_next++; + ldp [ 7] = *tmp_next++; + ldp [ 6] = *tmp_next++; + ldp [ 5] = *tmp_next++; + ldp [ 4] = *tmp_next++; + ldp [ 3] = *tmp_next++; + ldp [ 2] = *tmp_next++; + ldp [ 1] = *tmp_next++; + ldp [ 0] = *tmp_next++; + next = tmp_next; + } + return CORBA_B_TRUE; +} + + +CORBA_Boolean +CDR::skip_string () // ISO/1 or octet string +{ + CORBA_ULong len; + + if (get_ulong (len) == CORBA_B_FALSE || len > remaining) + return CORBA_B_FALSE; // buffer's changed + + next += (unsigned) len; + remaining -= (unsigned) len; + return CORBA_B_TRUE; +} + + +// +// Grow the CDR buffer, either to a known size (incoming message) or +// by a standard increment (creating outgoing message). +// +// We can't use realloc() because of a constraint that the part of the +// buffer into which we marshal be aligned according to MAX_ALIGNMENT, +// which can be a stronger requirement than malloc/realloc places on +// buffer. This makes growing a buffer on the encode side costly, since +// it can need to be done repetitively and copies more data each time. +// +// NOTE: this code knows about what's involved in the constructor and +// destructor, as it needs to invoke the constructor and do what the +// destructor would do (and not in the normal order). It also knows +// all other state that's significant. Change with care! +// +// NOTE: arguably this is a good place to ensure that the memory's zeroed +// out to comply with Orange Book C2 "object reuse" (meaning data, like +// I/O buffers) policy. IIOP doesn't mandate such policies though. +// +CORBA_Boolean +CDR::grow (size_t newsize) +{ + unsigned char *old_realbuf, *oldbuf; + size_t offset; + int old_do_swap = do_byteswap; + + // + // Iff old buffer was heap allocated, it gets freed soon. In any case, + // we need to know which bytes that have been marshaled or read thus + // far, so they'll also be in the newly grown buffer. + // + if (do_free) + old_realbuf = real_buffer; + else + old_realbuf = 0; + oldbuf = buffer; + assert ((next - buffer) < UINT_MAX); + offset = (unsigned) (next - buffer); + + // + // Calculate the new buffer's length; if growing for encode, we + // don't grow in "small" chunks because of the cost. + // + size_t new_len; + + if (newsize == 0) { + if (length < 4096) + new_len = length + 4096; + else + new_len = length * 2; + } else if (newsize <= length) { + return CORBA_B_TRUE; + } else + new_len = newsize; + + // + // Get a new buffer that's adequately aligned, and use it to + // reinitialize ourselves with the "free this buffer later" flag. + // + unsigned char *new_buffer; + + new_len += MAX_ALIGNMENT - 1; + if ((new_buffer = (unsigned char *) malloc (new_len)) == 0) + return CORBA_B_FALSE; + + (void) new (this) CDR (new_buffer, new_len, MY_BYTE_SEX, 1); + + // + // Now restore all the relevant old state that we saved earlier, + // and free the original buffer if needed. (The first buffer is + // normally stack-allocated and so mustn't be freed this way.) + // + do_byteswap = old_do_swap; + memcpy (buffer, oldbuf, offset); + skip_bytes (offset); + + if (old_realbuf) + free ((char *) old_realbuf); + + return CORBA_B_TRUE; +} diff --git a/TAO/IIOP/lib/runtime/cdr.hh b/TAO/IIOP/lib/runtime/cdr.hh new file mode 100644 index 00000000000..61120e0d247 --- /dev/null +++ b/TAO/IIOP/lib/runtime/cdr.hh @@ -0,0 +1,360 @@ +// @(#)cdr.hh 1.2 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// CDR: Common Data Representation (CDR) marshaling streams. +// +// This implementation assumes that the native numeric representation +// is two's complement for integers, IEEE single/double for floats. Also +// that characters are in ISO Latin/1. +// +// Note that CDR itself makes no such assumptions, but this implementation +// makes such assumptions for reasons of efficiency. Careful enhancements +// could preserve that efficiency where the assumptions are true, yet +// still allow the code to work when they aren't true. +// +// The implementation expects that buffers are aligned according to the +// strongest CDR alignment restriction. +// +// NOTE: this does everything "CDR 1.1" does ... that is, it supports +// the five extended OMG-IDL data types in UNO Appendix A, which provide +// richer arithmetic types (64 bit integers, "quad precision" FP) and +// UNICODE-based characters and strings. Those types are not standard +// parts of OMG-IDL at this time. +// +// THREADING NOTE: CDR data structures must be protected against +// concurrent access by their owning thread. +// + + +#ifndef _CDR_HH +#define _CDR_HH + +#include <assert.h> + +#include <corba/orb.hh> + +#include <runtime/align.hh> + + +// +// Identify byte order ... this is basically dependent on processor, but some +// processors support different byte orders (e.g. MIPS, UltraSPARC, PowerPC) +// as required for different software environments. +// +// We currently get this information through the "configure" script, which +// must (!!) be run before source code can be correctly compiled; too bad +// we have no way to ensure that the machine on which "configure" ran is +// the same as the one on which "make" will be run. +// + +#undef LITTLE_ENDIAN +#undef BIG_ENDIAN +#undef MY_BYTE_SEX + +// +// PC compilers normally don't assume they'll be given code that runs +// on other architectures; cope with that limitation once here, so that +// only truly compiler-specific code needs to use their predefines. +// +// NOTE: should really be using "__i386" to be strictly ANSI compliant. +// +#if defined (__BORLANDC__) || defined (_M_IX86) +# if !defined (i386) +# define i386 +# undef WORDS_BIGENDIAN +# endif +#endif + +// +// Yes, we assume no bizarre mixed-endian "nuxi" machine, just as we +// assume only IEEE arithmetic. +// +#if defined (WORDS_BIGENDIAN) +# define BIG_ENDIAN +# define MY_BYTE_SEX 0 +#else +# define LITTLE_ENDIAN +# define MY_BYTE_SEX 1 +#endif + +// +// The core marshaling primitive: a memory buffer, into which all the basic +// OMG-IDL datatypes can be placed ... or from which they can be retreived. +// +// A particularly useful static member function for this buffer is an +// interpretive encoding routine, usable as a typecode interpreter callback. +// Ditto for decoding. These are used to support all OMG-IDL datatypes, +// even those not supported directly by put/get primitives. +// +// Struct members are intentionally exposed; the functionality of this +// class, and hence the appropriate abstactions for them, hasn't quite +// settled down enough to settle on fast abstractions that let data be +// hidden without pointlessly sacrificing speed. +// +struct _EXPCLASS CDR +{ + // + // Define these constants as enums to ensure they get inlined + // and to avoid pointless static memory allocations. + // + enum { + // + // Constants defined by the CDR protocol. Note that some of these + // get reused as part of the standard binary format: unsigned is the + // same size as its signed cousin, float is CDR_LONG_SIZE, and double + // is CDR_LONGLONG_SIZE. + // + SHORT_SIZE = 2, + LONG_SIZE = 4, + LONGLONG_SIZE = 8, + LONGDOUBLE_SIZE = 16, + + // + // maximal CDR 1.1 alignment: "quad precision" FP + // (i.e. "long double", size as above) + // + MAX_ALIGNMENT = 16, + + // + // Default buffer size for request/response messages. These are + // normally stack-allocated, and tuning may cause you to want to + // change this value. The best value depends on your particular + // application mix; you can also change how buffers grow(). Most + // remote invocations (statistically) are "small", and the default + // used here is perhaps larger than most such messages. + // + // If this size is "too small" you need to heap-allocate buffers too + // often. "Too large" is mostly a waste of stackspace, but stack + // frames as large as the system page size (often 4Kb) can easily + // overrun the "redzone" at the bottom of most VM-based stacks. + // + DEFAULT_BUFSIZE = 1430 // Ethernet MTU, less headers + }; + + // + // ENCODING SUPPORT ... adjust pointers as needed, then store in the + // native byte order. + // + // There exist only routines to put byte, halfword (2 bytes), word + // (4 bytes), doubleword (8 bytes) and quadword (16 byte) entities, + // plus the interpretive encoder. + // + + CORBA_Boolean put_byte (char c); + CORBA_Boolean put_short (CORBA_Short s); + CORBA_Boolean put_long (CORBA_Long l); + CORBA_Boolean put_longlong (const CORBA_LongLong &ll); + + inline CORBA_Boolean put_char (CORBA_Char c) + { return put_byte ((char) c); } + inline CORBA_Boolean put_wchar (wchar_t wc) + { + // + // "wchar_t" isn't always 2 bytes, such + // systems might need further conversion + // (e.g. hosts with multibyte characters + // native, rather than UNICODE) + // + return put_short ((short)wc); + } + + inline CORBA_Boolean put_boolean (CORBA_Boolean b) + { return put_byte ((char) + (b != CORBA_B_FALSE)); } + + inline CORBA_Boolean put_octet (CORBA_Octet o) + { return put_byte ((char) o); } + inline CORBA_Boolean put_ushort (CORBA_UShort s) + { return put_short ((CORBA_Short) s); } + inline CORBA_Boolean put_ulong (CORBA_ULong l) + { return put_long ((CORBA_Long) l); } + inline CORBA_Boolean put_ulonglong (const CORBA_ULongLong &ll) + { return + put_longlong ((CORBA_LongLong &) ll); } + + inline CORBA_Boolean put_float (float f) + { return put_long (*(CORBA_Long *) &f); } + inline CORBA_Boolean put_double (const double &d) + { return + put_longlong (*(CORBA_LongLong *) &d); } + + CORBA_Boolean put_longdouble (CORBA_LongDouble &ld); + + // + // marshaling interpreter ... 'context' really points to a CDR. + // + static CORBA_TypeCode::traverse_status + encoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env + ); + + // + // DECODING SUPPORT ... same assumptions are made as above, but a + // flag is tested to determine whether decode should byteswap or not. + // It's cheaper to do it that way than to use virtual functions. + // + + CORBA_Boolean get_byte (char &c); + CORBA_Boolean get_short (CORBA_Short &s); + CORBA_Boolean get_long (CORBA_Long &l); + CORBA_Boolean get_longlong (CORBA_LongLong &ll); + + inline CORBA_Boolean get_char (CORBA_Char &o) + { return get_byte ((char &) o); } + inline CORBA_Boolean get_wchar (wchar_t &wc) + { + short s; + + // + // wchar_t isn't always "short" + // + CORBA_Boolean retval = get_short (s); + wc = s; + return retval; + } + + inline CORBA_Boolean get_boolean (CORBA_Boolean &b) + { + CORBA_Char c; + + // + // CORBA_Boolean is rarely 'char' + // + CORBA_Boolean retval = get_char (c); + b = (c == 1); + return retval; + } + + inline CORBA_Boolean get_octet (CORBA_Octet &o) + { return get_byte ((char &) o); } + inline CORBA_Boolean get_ushort (CORBA_UShort &s) + { return get_short ((short&) s); } + inline CORBA_Boolean get_ulong (CORBA_ULong &l) + { return get_long ((CORBA_Long &) l); } + inline CORBA_Boolean get_ulonglong (const CORBA_ULongLong &ull) + { return + get_longlong ((CORBA_LongLong &) ull); } + + inline CORBA_Boolean get_float (float &f) + { return get_long ((CORBA_Long &) f); } + inline CORBA_Boolean get_double (double &d) + { return + get_longlong ((CORBA_LongLong &) d); } + + CORBA_Boolean get_longdouble (CORBA_LongDouble &ld); + + // + // unmarshaling interpreter ... 'context' really points to a CDR. + // + static CORBA_TypeCode::traverse_status + decoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env + ); + + // + // Constructor ... buffer must be aligned for the strictest + // CDR alignment requirement, since the algorithms used here + // only maintain alignment with respect to &buffer [0]. + // + // Yes, that complicates the grow() primitive. + // + CDR ( + unsigned char *buf = 0, + unsigned len = 0, + int byte_order = MY_BYTE_SEX, + int consume_buf = 0 + ) : + real_buffer (buf), + do_free (consume_buf), + do_byteswap (byte_order != MY_BYTE_SEX) + { + ptr_arith_t temp = (ptr_arith_t) buf; + + temp += MAX_ALIGNMENT - 1; + temp &= ~((ptr_arith_t) MAX_ALIGNMENT - 1); + buffer = next = (unsigned char *) temp; + + length = remaining + = len - (unsigned) (buffer - real_buffer); + } + + ~CDR () + { if (do_free) delete real_buffer; } + + void *operator new (size_t, void *_FAR p) + { return p; } + void *operator new (size_t s) + { return ::operator new (s); } + void operator delete (void *p) + { ::operator delete (p); } + + // + // Used mostly when interpreting typecodes. These may change the + // state of a CDR buffer even when errors are reported. + // + CORBA_Boolean skip_string (); + CORBA_Boolean skip_bytes (unsigned nbytes) + { + if (remaining < nbytes) + return CORBA_B_FALSE; + remaining -= nbytes; + next += nbytes; + return CORBA_B_TRUE; + } + + // + // Also used when interpreting typecodes, but more generally + // when getting ready to read from encapsulations. In such + // cases the buffer alignment guarantees must be provided by + // the caller, this code doesn't verify them. These streams + // are "read only". + // + void setup_encapsulation ( + unsigned char *buf, + unsigned len + ) + { + next = buf + 1; + remaining = len - 1; + do_byteswap = (*buf != MY_BYTE_SEX); + do_free = 0; + } + + // + // Grow the buffer to the identified size ... if it's zero, just + // grow it by a standard quantum (e.g. when encoding we can't know + // in advance how big it will need to become). + // + CORBA_Boolean grow (size_t newlength); + + // + // Some code needs to know how much is left on encode or decode + // + size_t bytes_remaining () { return remaining; } + + // private: + // + // DATA MEMBERS ... + // + unsigned char *next; // next data goes here + size_t remaining; // space left + + unsigned char *real_buffer; // maybe not aligned + int do_free; + + unsigned char *buffer; + size_t length; + + int do_byteswap; // decode ONLY +}; + +#endif // _CDR_HH diff --git a/TAO/IIOP/lib/runtime/corbacom.cpp b/TAO/IIOP/lib/runtime/corbacom.cpp new file mode 100644 index 00000000000..b02bcbc149f --- /dev/null +++ b/TAO/IIOP/lib/runtime/corbacom.cpp @@ -0,0 +1,99 @@ +// @(#)corbacom.cpp 1.1 95/08/31 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: support for primitive data types +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#if defined (HAVE_WIDEC_H) +# include <widec.h> +#endif + + +// +// String utility support; this can need to be integrated with +// the ORB's own memory allocation subsystem. +// +CORBA_String +CORBA_string_alloc (CORBA_ULong len) +{ + return new CORBA_Char [(size_t)(len + 1)]; +} + +CORBA_String +CORBA_string_copy (const CORBA_Char *const str) +{ + if (!str) + return 0; + + CORBA_String retval = CORBA_string_alloc (strlen (str)); + + return strcpy (retval, str); +} + +void +CORBA_string_free (CORBA_Char *const str) +{ + delete str; +} + + +#if !defined (HAVE_WIDEC_H) +// +// NOTE: assuming that these don't exist unless they're declared in +// that header file ... +// + +extern "C" unsigned +wslen (const wchar_t *str) +{ + unsigned len = 0; + + while (*str++) + len++; + return len; +} + +extern "C" wchar_t * +wscpy (wchar_t *dest, const wchar_t *src) +{ + wchar_t *retval = dest; + + while ((*dest++ = *src++) != 0) + continue; + return retval; +} + +#endif // HAVE_WIDEC_H + +// +// Wide Character string utility support; this can need to be +// integrated with the ORB's own memory allocation subsystem. +// +CORBA_WString +CORBA_wstring_alloc (CORBA_ULong len) +{ + return new CORBA_WChar [(size_t) (len + 1)]; +} + +CORBA_WString +CORBA_wstring_copy (const CORBA_WChar *const str) +{ + if (*str) + return 0; + + CORBA_WString retval = CORBA_wstring_alloc (wslen (str)); + return wscpy (retval, str); +} + +void +CORBA_wstring_free (CORBA_WChar *const str) +{ + delete str; +} + diff --git a/TAO/IIOP/lib/runtime/debug.cpp b/TAO/IIOP/lib/runtime/debug.cpp new file mode 100644 index 00000000000..a5e1fe4138d --- /dev/null +++ b/TAO/IIOP/lib/runtime/debug.cpp @@ -0,0 +1,337 @@ +// @(#)debug.cpp 1.3 95/10/02 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: Simple debug/trace support +// +// THREADING NOTE: the global values here (debug_{level,filter,stream) are +// assumed to be modified "safely", e.g. in the main thread as part of +// process initialization. They are treated as immutable values through +// all of this debuging package. +// +// XXX on Windows, make it always use OutputDebugString() instead of stdio +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/thread.hh" +#include "runtime/debug.hh" + +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> + +#if unix +#include <sys/types.h> + +#elif defined (_WIN32) +# include <windows.h> + +#endif + + + + +#ifndef _POSIX_THREADS // _POSIX_THREAD_SAFE_FUNCTIONS implied +#define flockfile(f) +#define funlockfile(f) +#endif // _POSIX_THREADS + +unsigned debug_level = 0; +char *debug_filter = "l"; +static FILE *debug_stream = stderr; + + +// +// The rest of this file is not needed without -DDEBUG, and unless the +// vfprintf() call is available it can't work. +// +// NOTE: some older platforms have "_doprnt" that provides much the +// same functionality ... this could be modified to use that routine +// where it's available. +// +#if defined (DEBUG) && defined (HAVE_VPRINTF) + +// +// Support for prefixing debug messages with process ID and, if threaded, +// the thread ID. This lets messages from different sources be safely +// disentangled, even though they're interspersed in the output stream. +// +static pid_t my_pid; + +#if unix + +#ifdef _POSIX_THREADS +// +// Use POSIX initialization support to initialize just once. +// +static pthread_once_t debug_init = PTHREAD_ONCE_INIT; + +#define setup() pthread_once(&debug_init, setup_once) + +static void +setup_once () +{ + my_pid = getpid (); +} + +static void +emit_prefix (FILE *stream) +{ + pthread_t self = pthread_self (); + + fprintf (stream, "p%ld t%ld: ", (long) my_pid, (long) self); +} + +#else // !defined (_POSIX_THREADS) + +// +// Without threads, guard initialization so it can be repeated, +// and don't emit the thread ID in the messages. +// +static void +setup () +{ + if (my_pid == 0) { + my_pid = getpid (); + // other setup goes here + } +} + +#define emit_prefix(stream) fprintf (stream, "p%ld: ", (long) my_pid) +#endif // !_POSIX_THREADS + +#elif defined (_WIN32) + +// +// Not all implementations of Win32 have threads, but in any case +// this code doesn't yet support Win32 threads. +// +static void +setup () +{ + if (my_pid == 0) { + my_pid = GetCurrentProcessId (); + // other setup goes here + } +} + +#define emit_prefix(stream) fprintf (stream, "p%ld: ", my_pid) + +#else + +# error "unknown OS platform" +#endif // OS-specific initialization + + +void _EXPFUNC +dmsg_filter ( + const char *_FAR categories, + const char *_FAR fmt, + ... +) +{ + const char *cp; + + if (!categories || !debug_filter) + return; + + if (*debug_filter != '*') { // filter with "*" --> all pass + for (cp = categories; *cp; cp++) + if (strchr (debug_filter, *cp) != 0) + break; + + if (!*cp) + return; + } + + va_list ap; + + setup (); + flockfile (debug_stream); + emit_prefix (debug_stream); + + switch (*cp) { // standard categories + case 'l': fprintf (debug_stream, "(LEAK) "); break; + } + va_start (ap, fmt); + vfprintf (debug_stream, fmt, ap); + va_end (ap); + if (strchr (fmt, '\n') == 0) + fprintf (debug_stream, "\n"); + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called dmsg_filter\n"); // experimental +#endif +} + +void _EXPFUNC +dmsg_filter (unsigned level, const char *_FAR fmt, ...) +{ + if (level > debug_level) + return; + + va_list ap; + + setup (); + flockfile (debug_stream); + emit_prefix (debug_stream); + va_start (ap, fmt); + vfprintf (debug_stream, fmt, ap); + va_end (ap); + if (strchr (fmt, '\n') == 0) + fprintf (debug_stream, "\n"); + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called dmsg_filter\n"); // experimental +#endif +} + +void _EXPFUNC +dmsg_v (const char *_FAR fmt, ...) +{ + va_list ap; + + setup (); + flockfile (debug_stream); + emit_prefix (debug_stream); + va_start (ap, fmt); + vfprintf (debug_stream, fmt, ap); + va_end (ap); + if (strchr (fmt, '\n') == 0) + fprintf (debug_stream, "\n"); + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called dmsg_v\n"); // experimental +#endif +} + +void _EXPFUNC +_dmsg_x ( + CORBA_Environment _FAR &env, + const char *_FAR info +) +{ + const CORBA_Exception *ex = env.exception (); + + setup (); + flockfile (debug_stream); + emit_prefix (debug_stream); + fprintf (debug_stream, "exception '%s' at '%s'\n", ex->id (), info); + if (env.exception_type () == SYSTEM_EXCEPTION) { + CORBA_SystemException *sysex = (CORBA_SystemException *)ex; + + emit_prefix (debug_stream); + fprintf (debug_stream, "minor %#lx, completion %#lx\n", + sysex->minor (), (long) sysex->completion ()); + } + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called _dmsg_x\n"); // experimental +#endif +} + +void _EXPFUNC +dmsg_opaque ( + char *_FAR label, + unsigned char *_FAR buffer, + unsigned long len) +{ + setup (); + flockfile (debug_stream); + emit_prefix (debug_stream); + fprintf (debug_stream, "%s ", label); + + if (len == 0 || !buffer) + fprintf (debug_stream, "(empty)"); + else if (len > UINT_MAX) + fprintf (debug_stream, "Oversized opaque data: %ld bytes", len); + else { + unsigned i; + + for (i = 0; i < len; i++) + if (!isprint (buffer [i])) + break; + + if (i < len) { + if (len >= 20) + fprintf (debug_stream, "%ld bytes binary data", len); + else { + fprintf (debug_stream, "binary data {%2X", buffer [0]); + for (i = 1; i < len; i++) + fprintf (debug_stream, ", %2x", buffer [i]); + fprintf (debug_stream, "}"); + } + } else { + if (len >= 50) + fprintf (debug_stream, "%ld bytes string data", len); + else + fprintf (debug_stream, "string data { \"%.*s\" }", + (int) len, buffer); + } + } + fprintf (debug_stream, "\n"); + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called dmsg_opaque\n"); // experimental +#endif +} + +void _EXPFUNC +dmsg_opaque_full ( + char *_FAR label, + const unsigned char *_FAR buffer, + unsigned long len) +{ + setup (); + flockfile (debug_stream); + + emit_prefix (debug_stream); + fprintf (debug_stream, "%s ", label); + + if (len == 0 || !buffer) + fprintf (debug_stream, "(empty)"); + else { + unsigned i; + + for (i = 0; i < len; i++) + { + if (i == 0) + fprintf(debug_stream, "\nhex: "); + else if ((i % 32) == 0) + fprintf(debug_stream, "\n "); + else if ((i % 4) == 0) + fprintf(debug_stream, " "); + fprintf(debug_stream, "%02x", buffer[i]); + } + for (i = 0; i < len; i++) + { + if (i == 0) + fprintf(debug_stream, "\nchars: "); + else if ((i % 32) == 0) + fprintf(debug_stream, "\n "); + else if ((i % 4) == 0) + fprintf(debug_stream, " "); + fprintf(debug_stream, "%c ", + (isprint(buffer[i]) ? buffer[i] : '?')); + + } + fprintf(debug_stream, "\n"); + } + fprintf (debug_stream, "\n"); + funlockfile (debug_stream); + +#if defined (_WIN32) + OutputDebugString ("called dmsg_opaque_full\n"); // experimental +#endif +} + +#endif // DEBUG && HAVE_VPRINTF diff --git a/TAO/IIOP/lib/runtime/debug.hh b/TAO/IIOP/lib/runtime/debug.hh new file mode 100644 index 00000000000..55662c37a88 --- /dev/null +++ b/TAO/IIOP/lib/runtime/debug.hh @@ -0,0 +1,104 @@ +// @(#)debug.hh 1.1 95/08/31 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// debug/trace support. +// + +#ifndef __DEBUG_HH +#define __DEBUG_HH + +#ifndef DECLARED_STRERROR +extern "C" char *strerror (int); +#endif // DECLARED_STRERROR + +// +// These are global to simplify is use by other code, very much +// in particular by getopt and related argument-parsing code +// +// THREADING NOTE: don't set them except in an unthreaded environment +// such as process initialization. They're treated as immutable. +// +extern unsigned debug_level; // 0 to ??; higher == more +extern char *debug_filter; // set by getopt + + +// +// These are just simple 0, 1, and 2 argument messages that +// will appear when debugging's enabled, regardless of category. +// They also just compile out painlessly. +// +#ifdef DEBUG +#include <stdio.h> +#include <errno.h> +#include <string.h> + +// 1, 2, 3 argument messages -- generic +#define dmsg(s) { if (debug_level) dmsg_v (s); } +#define dmsg1(s,a1) { if (debug_level) dmsg_v (s, a1); } +#define dmsg2(s,a1,a2) { if (debug_level) dmsg_v (s, a1, a2); } + +// dump CORBA_Exception, if any, with id tag +#define dexc(env,s) { if (debug_level && env.exception ()) \ + _dmsg_x (env, s); } + +// dump POSIX error indication, if any, with ID tag +#define dperror(str) { if (debug_level) dmsg_v ("%s: %s", \ + str, strerror (errno)); } + +// dump socket error indication, if any, with ID tag +#ifdef _WINSOCKAPI_ +#define dsockerr(s) { if (debug_level) dmsg_v ("%s: winsock error %d", \ + s, WSAGetLastError()); } +#else +#define dsockerr(s) dperror(s) +#endif + +#else // !DEBUG +#define dmsg(s) { } +#define dmsg1(s,a1) { } +#define dmsg2(s,a1,a2) { } + +#define dexc(env, s) { } +#define dperror(s) { } +#define dsockerr(s) { } +#endif // DEBUG + + +// +// These don't compile out; you must #ifdef them. This is done +// intentionally since CPP macros have severe limits, and varargs +// (or lack thereof) is one of them. +// + +#ifdef DEBUG +// +// This is like an fprintf statement except the filter is a set of +// characters (string). If debug_level is nonzero and any characters +// in that string are in the "debug_filter" string, the message +// is then printed. Assign thosee characters as needed. +// +extern void _EXPFUNC dmsg_filter (const char *_FAR filter, + const char *_FAR fmt, ...); + +// +// Filter according to debug_level instead of category. +// (For speed, test against debug_level directly.) +// +extern void _EXPFUNC dmsg_filter (unsigned level, const char *_FAR fmt, ...); + +// +// General varargs debug message printer, no filtering +// +extern void _EXPFUNC dmsg_v (const char *_FAR fmt, ...); +extern void _EXPFUNC _dmsg_x (CORBA_Environment _FAR &env, + const char *_FAR info); +extern void _EXPFUNC dmsg_opaque (char *_FAR label, + unsigned char *_FAR buffer, + unsigned long len); +extern void _EXPFUNC dmsg_opaque_full (char *_FAR label, + const unsigned char *_FAR buffer, + unsigned long len); +#endif // DEBUG + +#endif // __DEBUG_HH diff --git a/TAO/IIOP/lib/runtime/except.cpp b/TAO/IIOP/lib/runtime/except.cpp new file mode 100644 index 00000000000..3c21f0c1a53 --- /dev/null +++ b/TAO/IIOP/lib/runtime/except.cpp @@ -0,0 +1,535 @@ +// @(#)except.cpp 1.11 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: Exception handling support +// +// THREADING NOTE: calling thread handles mutual exclusion policy +// on all of these data structures. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> +#include <corba/orb.hh> + +#include "runtime/cdr.hh" +#include "runtime/debug.hh" +#include "runtime/thread.hh" + +#include <initguid.h> + +#if defined (HAVE_WIDEC_H) +# include <widec.h> +#endif + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t except_lock = PTHREAD_MUTEX_INITIALIZER; + +#else + + // stub out these _POSIX_THREAD_SAFE_FUNCTIONS +# define flockfile(f) +# define funlockfile(f) +#endif // _POSIX_THREADS + + + + +// {77420082-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_Exception, +0x77420082, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + +// {77420083-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_UserException, +0x77420083, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + +// {77420084-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_SystemException, +0x77420084, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +CORBA_Exception::CORBA_Exception ( + CORBA_TypeCode_ptr tc +) : + _type (tc), + _refcnt (1) +{ + if (_type) + _type->AddRef (); + assert (_type != 0); + assert (_refcnt > 0); +} + +CORBA_Exception::CORBA_Exception ( + const CORBA_Exception &src +) : + _type (src._type), + _refcnt (1) +{ + if (_type) + _type->AddRef (); + assert (_type != 0); + assert (_refcnt > 0); +} + +// +// NOTE: It's this code, not anything defined in a subclass, which +// is responsible for releasing any storage owned by the exception. +// It can do this because it's got the typecode. +// +CORBA_Exception::~CORBA_Exception () +{ + assert (_refcnt == 0); + assert (_type != 0); + + assert (1 == 2); +} + +CORBA_Exception & +CORBA_Exception::operator = ( + const CORBA_Exception &src +) +{ + if (_type) + _type->Release (); + _type = src._type; + if (_type) + _type->AddRef (); + assert (_type != 0); + assert (_refcnt > 0); + + return *this; +} + +const CORBA_String +CORBA_Exception::id () const +{ + CORBA_Environment env; + + assert (_refcnt > 0); + if (_type) + return _type->id (env); + else + return 0; +} + +const CORBA_TypeCode_ptr +CORBA_Exception::type () const +{ + assert (_refcnt > 0); + return _type; +} + +// +// For COM -- IUnKnown operations +// +ULONG +__stdcall +CORBA_Exception::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&except_lock); +#endif + + assert (_refcnt > 0); + return ++_refcnt; +} + +ULONG +__stdcall +CORBA_Exception::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&except_lock); +#endif + + assert (_refcnt > 0); + _refcnt--; + if (_refcnt != 0) + return _refcnt; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + // CORBA_TypeCode_ptr tc = _type->_duplicate (); + + { CORBA_Any free_it_all (_type, this, CORBA_B_TRUE); } + + // tc->Release (); + + return 0; +} + +HRESULT +__stdcall +CORBA_Exception::QueryInterface ( + REFIID riid, + void **ppv +) +{ + assert (_refcnt > 0); + *ppv = 0; + + if (IID_CORBA_Exception == riid || IID_IUnknown == riid) + *ppv = this; + + // + // XXX this approach needs modifying to enable returning + // UserException, SystemException, and other kinds of pointers. + // + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + + +// +// Avoid zillions of not-quite-inlined copies of utilities. +// +CORBA_UserException::CORBA_UserException ( + CORBA_TypeCode_ptr tc +) : + CORBA_Exception (tc) +{ +} + +CORBA_UserException::~CORBA_UserException () +{ +} + +CORBA_SystemException::CORBA_SystemException ( + CORBA_TypeCode_ptr tc, + CORBA_ULong code, + CORBA_CompletionStatus completed +) : + _minor (code), + _completed (completed), + CORBA_Exception (tc) +{ +} + +CORBA_SystemException::~CORBA_SystemException () +{ +} + + +#define NUM_SYS_EXCEPTIONS 26 // update correctly! +#define TC_BUFLEN 160 // preallocated tc buffer + +static CORBA_TypeCode_ptr sys_exceptions [NUM_SYS_EXCEPTIONS]; +CORBA_ExceptionList __system_exceptions; + +// +// Make the TypeCode for a standard exception ... note that "buffer" +// holds the (unscoped) name originally, and is then overwritten. +// +// When used correctly, initializing system exceptions is only an +// exercise in CPU time; it allocates no new memory. +// +static void +make_standard_typecode ( + CORBA_TypeCode_ptr tcp, + const char *name, + unsigned char *buffer, + size_t buflen, + CORBA_Environment &env +) +{ + static const char *minor = "minor"; + static const char *completion = "completion"; + + static const unsigned long oc_completion_status [] = { + 1, // byte order flag, tricky + 0, 0, // type ID omitted + 3, // three members + 0, 0, // ... whose names are all omitted + 0, 0, + 0, 0 + }; + static CORBA_TypeCode + tc_completion_status (tk_enum, sizeof oc_completion_status, + (unsigned char *)&oc_completion_status, CORBA_B_FALSE); + static const CORBA_TypeCode_ptr completion_status = &tc_completion_status; + + + // + // Create a CDR stream ... juggle the alignment here a bit, we + // know it's good enough for tye typecode. + // + CDR stream (0, buflen); + + stream.next = stream.buffer = buffer; + + // + // into CDR stream, stuff (in order): + // - byte order flag [4 bytes] + // - exception ID [27 + N bytes] + // - exception name [4 + N bytes ] + // - number of members (2) [4 bytes ] + // - foreach member, { name string, typecode } [~40 bytes] + // + char full_id [100], *strptr = (char *) &full_id; + + (void) sprintf (full_id, "IDL:omg.org/CORBA/%s:1.0", name); + assert (strlen (full_id) <= sizeof full_id); + + if (stream.put_byte (MY_BYTE_SEX) != CORBA_B_TRUE + || CDR::encoder (_tc_CORBA_String, &strptr, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_String, &name, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || stream.put_ulong (2L) != CORBA_B_TRUE + || CDR::encoder (_tc_CORBA_String, &minor, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_TypeCode, &_tc_CORBA_ULong, 0, + &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_String, &completion, 0, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || CDR::encoder (_tc_CORBA_TypeCode, &completion_status, 0, + &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return; + } + + // + // OK, we stuffed the buffer we were given (or grew a bigger one; + // hope to avoid that during initialization). Now build and return + // a TypeCode, saving it away in the list of ones that the ORB will + // always accept as part of any operation response! + // + sys_exceptions [__system_exceptions.length++] + = new (tcp) CORBA_TypeCode (tk_except, + stream.next - stream.buffer, + stream.buffer, CORBA_B_FALSE); + + assert (tcp->_length <= TC_BUFLEN); + return; +} + + +// +// List of standard/system exceptions ... used to create static storage +// for their typecodes, then later to initialize that storage using the +// routine above. (It's just too painful to init these typecodes +// statically in all cases!) +// +#define STANDARD_EXCEPTION_LIST \ + SYSEX (UNKNOWN) \ + SYSEX (BAD_PARAM) \ + SYSEX (NO_MEMORY) \ + SYSEX (IMP_LIMIT) \ + SYSEX (COMM_FAILURE) \ + SYSEX (INV_OBJREF) \ + SYSEX (OBJECT_NOT_EXIST) \ + SYSEX (NO_PERMISSION) \ + SYSEX (INTERNAL) \ + SYSEX (MARSHAL) \ + SYSEX (INITIALIZE) \ + SYSEX (NO_IMPLEMENT) \ + SYSEX (BAD_TYPECODE) \ + SYSEX (BAD_OPERATION) \ + SYSEX (NO_RESOURCES) \ + SYSEX (NO_RESPONSE) \ + SYSEX (PERSIST_STORE) \ + SYSEX (BAD_INV_ORDER) \ + SYSEX (TRANSIENT) \ + SYSEX (FREE_MEM) \ + SYSEX (INV_IDENT) \ + SYSEX (INV_FLAG) \ + SYSEX (INTF_REPOS) \ + SYSEX (BAD_CONTEXT) \ + SYSEX (OBJ_ADAPTER) \ + SYSEX (DATA_CONVERSION) + +// +// Declare static storage for these ... the buffer is "naturally" +// aligned and overwritten. +// +// XXX this actually doesn't guarantee "natural" alignment, but +// it works that way in most systems. +// +#define SYSEX(name) \ + static long tc_buf_ ## name [TC_BUFLEN / sizeof (long)]; \ + static CORBA_TypeCode tc_std_ ## name (tk_except); \ + CORBA_TypeCode_ptr _tc_CORBA_ ## name = &tc_std_ ## name; +STANDARD_EXCEPTION_LIST +#undef SYSEX + + +// +// Runtime initialization of all standard exception typecodes. +// Called from CORBA::ORB::init(). +// +void +__TC_init_standard_exceptions (CORBA_Environment &env) +{ + // + // Initialize the list of system exceptions, used when + // unmarshaling. + // + __system_exceptions.length = 0; + __system_exceptions.maximum = NUM_SYS_EXCEPTIONS; + __system_exceptions.buffer = &sys_exceptions [0]; + + // + // Initialize the typecodes. + // +#define SYSEX(name) \ + if (env.exception () == 0) \ + make_standard_typecode (&tc_std_ ## name, #name, \ + (unsigned char *) tc_buf_ ## name, \ + sizeof tc_buf_ ## name, env); + + STANDARD_EXCEPTION_LIST +#undef SYSEX +} + +#undef STANDARD_EXCEPTION_LIST + +// +// Static initialization of the two user-defined exceptions that +// are part of the ORB. +// +static CORBA_Octet tc_buf_Bounds [] = { + 0, 0, 0, 0, // big endian, padded + 0, 0, 0, 38, // strlen (id) + 1 + 'I', 'D', 'L', ':', + 'o', 'm', 'g', '.', + 'o', 'r', 'g', '/', + 'C', 'O', 'R', 'B', + 'A', '/', 'T', 'y', + 'p', 'e', 'C', 'o', + 'd', 'e', '/', 'B', + 'o', 'u', 'n', 'd', + 's', ':', '1', '.', + '0', '\0', 0, 0, + 0, 0, 0, 0 // no members to this typecode +}; +static CORBA_TypeCode tc_std_Bounds (tk_except, sizeof tc_buf_Bounds, + tc_buf_Bounds, CORBA_B_FALSE ); +CORBA_TypeCode_ptr _tc_CORBA_Bounds = &tc_std_Bounds; + +static CORBA_Octet tc_buf_BadKind [] = { + 0, 0, 0, 0, // big endian, padded + 0, 0, 0, 39, // strlen (id) + 1 + 'I', 'D', 'L', ':', + 'o', 'm', 'g', '.', + 'o', 'r', 'g', '/', + 'C', 'O', 'R', 'B', + 'A', '/', 'T', 'y', + 'p', 'e', 'C', 'o', + 'd', 'e', '/', 'B', + 'a', 'd', 'K', 'i', + 'n', 'd', ':', '1', + '.', '0', '\0', 0, + 0, 0, 0, 0 // no members to this typecode +}; +static CORBA_TypeCode tc_std_BadKind (tk_except, sizeof tc_buf_BadKind, + tc_buf_BadKind, CORBA_B_FALSE ); +CORBA_TypeCode_ptr _tc_CORBA_BadKind = &tc_std_BadKind; + + + +// +// Convenience -- say if the exception is a system exception or not. +// +CORBA_ExceptionType +CORBA_Environment::exception_type () const +{ + static char sysex_prefix [] = "IDL:omg.org/CORBA/"; + static char typecode_extra [] = "TypeCode/"; + + if (!_exception) + return NO_EXCEPTION; + + // + // All exceptions currently (CORBA 2.0) defined in the CORBA + // scope are system exceptions ... except for a couple that + // are related to TypeCodes. + // + char *id = _exception->id (); + + if (strncmp (id, sysex_prefix, sizeof sysex_prefix - 1) == 0 + && strncmp (id + sizeof sysex_prefix - 1, + typecode_extra, sizeof typecode_extra - 1) != 0) + return SYSTEM_EXCEPTION; + + return USER_EXCEPTION; +} + + +// +// Diagnostic utility routine: describe the exception onto +// the standard I/O stream passed as a parameter. +// +// XXX make this a member function on "Environment" +// +void _EXPFUNC +print_exception ( + const CORBA_Exception *x, + const char *info, + FILE *stream +) +{ + CORBA_String id = x->id (); + + flockfile (stream); + fprintf (stream, "EXCEPTION, %s\n", info); + + // + // XXX get rid of this logic, and rely on some member function + // on Exception to say if it's user or system exception. + // + if (strncmp ((char *) id, "IDL:omg.org/CORBA/", 10) == 0 + && strncmp ((char *) id, "IDL:omg.org/CORBA/TypeCode/", 19) != 0 + ) { + // + // XXX this should be a QueryInterface call instead + // + CORBA_SystemException *x2 = (CORBA_SystemException *)x; + + // + // XXX there are a other few "user exceptions" in the CORBA scope, + // they're not all standard/system exceptions ... really need + // to either compare exhaustively against all those IDs (yeech) + // or (preferably) to represent the exception type directly in + // the exception value so it can be queried. + // + + fprintf (stream, "system exception, ID '%s'\n", id); + fprintf (stream, "minor code = %#lx, completed = ", x2->minor ()); + switch (x2->completion ()) { + case COMPLETED_YES: + fputs ("YES", stream); + break; + case COMPLETED_NO: + fputs ("NO", stream); + break; + case COMPLETED_MAYBE: + fputs ("MAYBE", stream); + break; + default: + fputs ("**garbage**", stream); + break; + } + fputc ('\n', stream); + } else { + // + // XXX we can use the exception's typecode to dump all the data + // held within it ... + // + fprintf (stream, "user exception, ID '%s'\n", id); + } + funlockfile (stream); +} diff --git a/TAO/IIOP/lib/runtime/interp.cpp b/TAO/IIOP/lib/runtime/interp.cpp new file mode 100644 index 00000000000..abd6843421f --- /dev/null +++ b/TAO/IIOP/lib/runtime/interp.cpp @@ -0,0 +1,1515 @@ +// @(#)interp.cpp 1.4 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TYPECODE: interpreter, traverses data structures +// +// This uses the standard C/C++ representation for data, and knows how to +// do things like align and pad according to standard rules. It is driven +// by CDR marshaled representations of TypeCodes. +// +// It does two key things: (a) calculate size and alignment restrictions +// for the data type described by any given typecode; and (b) "visits" +// each element of a data type in the order those elements are defined +// in the type's IDL definition. +// +// A typical use is that some application-specific "visit" function will +// be called with a typecode and data value. Then that "visit" function +// may choose to use the interpreter's knowledge of the environment's +// size, padding, and alignment rules to help it examine each of the +// constituents of complex data values. It does so by making a call to +// TypeCode::traverse(), and passing itself for future recursive calls. +// +// +// NOTE that this module has system dependent parts, and so should be +// examined when porting to new CPU architectures, compilers, and so forth +// to make sure it correctly implements the appropriate binary interfaces. +// +// Issues of concern are primarily that sizes and representations of CORBA +// primitive data types are correct (key issues are verified when the +// ORB initializes) and that the alignment rules are recognized. +// +// Also, exceptions have vtables in them, which may cause trouble if they +// aren't located at the very beginning by the compiler in question. +// +// So for example, moving to another CPU architecture which still uses +// standard sized two's complement integers and IEEE floating point, and +// expects "natural" alignment, won't be hard. Even using PC style +// tightly packed data is simple; the alignment rules are just simpler. +// Most volume microprocessors used in 1995 are correctly supported. +// +// Using data representations that are far from the standard C/C++ style +// data layout is probably not practical with this implementation. LISP +// systems, as one example, probably won't use "in-memory" representations +// much like C/C++, even though its "wire form" could directly match CDR. +// +// +// ALSO, the treatment of exceptions may need to be examined in language +// environments which actually rely on C++ exceptions. The RTTI data that +// identifies exceptions can easily be ignored by this interpreter (if it's +// taught about that compiler's RTTI) but it may not be practical for any +// code not generated by that specific C++ compiler to store such data in +// the right place to look like a C++ exception, or to throw exceptions +// when that's needed. (RTTI == "Run Time Typing Information", needed to +// make C++ exceptions work correctly and partially exposed to users by +// the ANSI standards comittee. It provides type-safe "downcasting" and +// other features previously unavailable in C++.) +// +// +// THREADING NOTE: Data structures being traversed should only be modified +// by the thread doing the traversal. The interpretive code itself is +// reentrant (recursive!) so presents no threading issues; only the data +// being fed to the interpreter must be protected against concurrency. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/debug.hh" +#include "runtime/cdr.hh" + + + +// +// Utility routines are used to manipulate CDR-encapsulated TypeCode parameter +// lists, calculating the size and alignment of the data type being described. +// The TCKind value has always been removed from the CDR stream when these +// calculator routines get called. +// +typedef size_t +attribute_calculator ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +); + +static attribute_calculator calc_struct_attributes; +static attribute_calculator calc_exception_attributes; +static attribute_calculator calc_union_attributes; +static attribute_calculator calc_alias_attributes; +static attribute_calculator calc_array_attributes; + + +// +// Other utility routines are used to skip the parameter +// lists when they're not needed. +// +typedef CORBA_Boolean param_skip_rtn (CDR *); + +static CORBA_Boolean +skip_encapsulation (CDR *stream) +{ + return stream->skip_string (); +} + +static CORBA_Boolean +skip_long (CDR *stream) +{ + CORBA_ULong scratch; + + return stream->get_ulong (scratch); +} + + +// +// Table supporting calculation of size and alignment requirements for +// any one instance of a given data types. +// +// This is indexed via CDR's TCKind values, which are "frozen" as part of the +// CDR standard. Entries hold either the size and alignment values for that +// data type, or a pointer to a function that is used to calculate those +// values. Function pointers are normally needed only for constructed types. +// +// A "skipper" routine is provided for some data types whose size is known +// statically (e.g. objrefs, structures, strings) but whose typecodes have +// parameters that sometimes need to be ignored when found in a CDR stream. +// Any attribute calculator routine always skips parameters in the CDR input +// stream, so no type with such a routine also needs a "skipper". +// +// Rather than growing a set of processor-specific #ifdefs, we calculate +// most of this table (except functions) at ORB initialization time. +// +struct table_element { + size_t size; + size_t alignment; + attribute_calculator *calc; + param_skip_rtn *skipper; +}; + +static table_element +table [TC_KIND_COUNT] = { + { 0, 1, 0 }, // tk_null + { 0, 1, 0 }, // tk_void + + { 0, 1, 0, 0 }, // tk_short + { 0, 1, 0, 0 }, // tk_long + { 0, 1, 0, 0 }, // tk_ushort + { 0, 1, 0, 0 }, // tk_ulong + + { 0, 1, 0, 0 }, // tk_float + { 0, 1, 0, 0 }, // tk_double + + { 0, 1, 0, 0 }, // tk_boolean + { 0, 1, 0, 0 }, // tk_char + { 0, 1, 0, 0 }, // tk_octet + { 0, 1, 0, 0 }, // tk_any + + { 0, 1, 0, 0 }, // tk_TypeCode + { 0, 1, 0, 0 }, // tk_Principal + { 0, 1, 0, skip_encapsulation }, // tk_objref + + { 0, 1, calc_struct_attributes, 0 }, // tk_struct + { 0, 1, calc_union_attributes, 0 }, // tk_union + + { 0, 1, 0, skip_encapsulation }, // tk_enum + { 0, 1, 0, skip_long }, // tk_string + { 0, 1, 0, skip_encapsulation }, // tk_sequence + { 0, 1, calc_array_attributes, 0 }, // tk_array + + // + // Two TCKind values added in 94-11-7 + // + { 0, 1, calc_alias_attributes, 0 }, // tk_alias + { 0, 1, calc_exception_attributes, 0 }, // tk_except + + // + // Five extended IDL data types, defined in Appendix A of 94-9-32 + // but here with different numeric TCKind codes. These types + // represent extensions to CORBA (specifically, to IDL) which are + // not yet standardized. + // + { 0, 1, 0, 0 }, // tk_longlong + { 0, 1, 0, 0 }, // tk_ulonglong + { 0, 1, 0, 0 }, // tk_longdouble + { 0, 1, 0, 0 }, // tk_wchar + { 0, 1, 0, skip_long } // tk_wstring +}; + + +// +// Runtime initialization of the table above; note that this compiles down +// to a set of assignment statements, with the real work done by the C++ +// compiler when this file gets compiled. +// +// "Natural alignment" is a policy that the processor controls the alignment +// of data based on its type. There's variation; some CPUs have a maximum +// alignment requirement of two or four bytes, others have some type-specific +// exceptions to the normal "alignment == size" rule. +// +// "Fixed" alignment ignores data type when establishing alignment; not all +// processors support such policies, and those which do often pay a cost to +// do so (viz. RISC/CISC discussions). The primary example of an OS family +// that chose "fixed" alignment is Microsoft's x86 systems, which normally +// align on one byte boundaries to promote data space efficiency. +// +// +// NOTE: typical PC compiler options let you specify other alignments, but +// none are "natural". Also, they don't apply consistently to all data types. +// Change the "one byte" assumption with extreme caution! And make sure all +// header files (e.g. generated by an IDL compiler) make sure that alignment +// of IDL-defined data types is consistent (one byte). +// +#if unix +#define setup_entry(x,t) \ + { \ + struct align_struct_ ## t { \ + x one; \ + char dummy [17 - sizeof(x)]; \ + x two; \ + }; \ + \ + align_struct_ ## t align; \ + table [t].size = sizeof (x); \ + table [t].alignment = \ + (char*) &align.two - (char*) &align.one - 16; \ + } + +#else // PC "fixed" alignment +#define setup_entry(x,t) \ + { \ + table [t].size = sizeof (x); \ + table [t].alignment = 1; \ + } + +#endif + +// +// Fills in fixed size and alignment values. +// +void +__TC_init_table () +{ + setup_entry (CORBA_Short, tk_short); + setup_entry (CORBA_Long, tk_long); + setup_entry (CORBA_UShort, tk_ushort); + setup_entry (CORBA_ULong, tk_ulong); + + setup_entry (CORBA_Float, tk_float); + setup_entry (CORBA_Double, tk_double); + + setup_entry (CORBA_Boolean, tk_boolean); + setup_entry (CORBA_Char, tk_char); + setup_entry (CORBA_Octet, tk_octet); + setup_entry (CORBA_Any, tk_any); + + setup_entry (CORBA_TypeCode_ptr, tk_TypeCode); + setup_entry (CORBA_Principal_ptr, tk_Principal); + setup_entry (CORBA_Object_ptr, tk_objref); + + enum generic_enum {a, b, c, d}; + + // XXX workaround for G++ 2.6.3 bug + // setup_entry (generic_enum, tk_enum); + table [tk_enum].size = sizeof (generic_enum); + table [tk_enum].alignment = sizeof (generic_enum); + + setup_entry (CORBA_String, tk_string); + setup_entry (CORBA_OctetSeq, tk_sequence); + + setup_entry (CORBA_LongLong, tk_longlong); + setup_entry (CORBA_ULongLong, tk_ulonglong); + setup_entry (CORBA_LongDouble, tk_longdouble); + setup_entry (CORBA_WChar, tk_wchar); + setup_entry (CORBA_WString, tk_wstring); +} + +#undef setup + + +// +// For a given typecode, figure out its size and alignment needs. This +// version is used mostly when traversing other typecodes, and follows +// these rules: +// +// - Some typecodes are illegal (can't be nested inside others); +// - Indirections are allowed; +// - The whole typecode (including TCKind enum) is in the stream +// +// When the routine returns, the stream has skipped this TypeCode. +// +// "size" is returned, "alignment" is an 'out' parameter. If it is non-null, +// "tc" is initialized to hold the contents of the TypeCode; it depends on +// the contents of the original stream to be valid. +// +// XXX explore splitting apart returning the size/alignment data and the +// TypeCode initialization; union traversal would benefit a bit, but it +// would need more than that to make it as speedy as struct traversal. +// +static size_t +calc_nested_size_and_alignment ( + CORBA_TypeCode_ptr tc, + CDR *original_stream, + size_t &alignment, + CORBA_Environment &env +) +{ + // + // Get the "kind" ... if this is an indirection, this is a + // guess which will soon be updated. + // + CORBA_ULong temp; + CORBA_TCKind kind; + + if (original_stream->get_ulong (temp) == CORBA_B_FALSE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + env.clear (); + kind = (CORBA_TCKind) temp; + + // + // Check for indirection, setting up the right CDR stream to + // use when getting the rest of the parameters. (We rely on + // the fact that indirections may not point to indirections.) + // + CDR indirected_stream; + CDR *stream; + + if (kind == ~0) { + CORBA_Long offset; + + // + // Get indirection, sanity check it, set up new stream + // pointing there. + // + // XXX access to "real" size limit for this typecode and + // use it to check for errors before indirect and to limit + // the new stream's length. ULONG_MAX is too much! + // + if (!original_stream->get_long (offset) + || offset >= -8 + || ((-offset) & 0x03) != 0) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + offset -= 4; // correct for get_long update + + indirected_stream.next = original_stream->next + (ptr_arith_t) offset; + indirected_stream.remaining = (size_t) ULONG_MAX; + stream = &indirected_stream; + + // + // Fetch indirected-to TCKind, deducing byte order. + // + if (*indirected_stream.next == 0) // big-endian? + indirected_stream.do_byteswap = (MY_BYTE_SEX != 0); + else + indirected_stream.do_byteswap = (MY_BYTE_SEX == 0); + + if (!indirected_stream.get_ulong (temp)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + kind = (CORBA_TCKind) temp; + + } else + stream = original_stream; + + // + // Check for illegal TCKind enum values ... out of range, or which + // represent data values that can't be nested. (Some can't even + // exist freestanding!) + // + if (kind >= TC_KIND_COUNT + || kind <= tk_void + || kind == tk_except) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Use attribute calculator routine if it exists; these are needed + // only for variable-sized data types, with encapsulated parameter + // lists that affect the size and alignment of "top level" memory + // needed to hold an instance of this type. + // + if (table [kind].calc != 0) { + assert (table [kind].size == 0); + + // + // Pull encapsulation length out of the stream. + // + if (stream->get_ulong (temp) == CORBA_B_FALSE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Initialize the TypeCode if requested + // + if (tc) { + tc->_kind = kind; + tc->_buffer = stream->next; + tc->_length = temp; + } + + // + // Set up a separate stream for the parameters; it may easily + // have a different byte order, and this is as simple a way + // as any to ensure correctness. Then use the calculator + // routine to calculate size and alignment. + // + CDR sub_encapsulation; + size_t size; + + assert (temp <= UINT_MAX); + sub_encapsulation.setup_encapsulation (stream->next, (size_t) temp); + size = table [kind].calc (&sub_encapsulation, alignment, env); + + // + // Check for garbage at end of parameter lists, or other cases + // where parameters and the size allocated to them don't jive. + // + stream->skip_bytes ((unsigned) temp); + if (stream->next != sub_encapsulation.next) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + return size; + } + assert (table [kind].size != 0); // fixed size data type + + // + // Reinitialize the TypeCode if requested; this consumes any TypeCode + // parameters in the stream. They only exist for TCKind values that + // have parameters, but which represent fixed-size data types in the + // binary representation: tk_string, tk_wstring, tk_objref, tk_enum, + // and tk_sequence. + // + if (tc) { + CORBA_ULong len; + + tc->_kind = kind; + switch (kind) { + default: + assert (table [kind].skipper == 0); + break; + + case tk_string: + case tk_wstring: + if (stream->get_ulong (len) == CORBA_B_FALSE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + tc->_length = len; + break; + + case tk_enum: + case tk_objref: + case tk_sequence: + if (stream->get_ulong (len) == CORBA_B_FALSE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + tc->_length = len; + + assert (len < UINT_MAX); + tc->_buffer = stream->next; + stream->skip_bytes ((unsigned) len); + break; + } + + // + // Otherwise, consume any parameters without stuffing them into + // a temporary TypeCode. + // + } else if (table [kind].skipper != 0 + && table [kind].skipper (stream) == CORBA_B_FALSE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Return statically known values. + // + alignment = table [kind].alignment; + return table [kind].size; +} + + +// +// Given typecode bytes for a structure (or exception), figure out its +// alignment and size; return size, alignment is an 'out' parameter. +// Only "tk_struct" (or "tk_except") has been taken out of the stream +// parameter holding the bytes. +// +// We use a one-pass algorithm, calculating size and inter-element +// padding while recording the strongest alignment restriction. Then +// we correct the size to account for tail-padding. +// +// This routine recognizes that exceptions are just structs with some +// additional information. Different environments may differ in what +// that additional information is, so this routine may need to be taught +// about compiler-specific representation of that additional "RTTI" data. +// +static size_t +calc_struct_and_except_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Boolean is_exception, + CORBA_Environment &env +) +{ + CORBA_ULong members; + size_t size; + + // + // Exceptions are like structs, with key additions (all of which + // might need to be be applied to structures!): vtable, typecode, + // and refcount. The size must include these "hidden" members. + // + // NOTE: in environments with "true" C++ exceptions, there may + // need to be a slot for additional "RTTI" information; maybe it + // is part of the vtable, or maybe not. Or, that information + // (needed to determine which 'catch' clauses apply) may only be + // provided by the compiler to the runtime support for the "throw" + // statement. + // + if (is_exception) { + size = sizeof (CORBA_Exception); + alignment = table [tk_TypeCode].alignment; + } else { + alignment = 1; + size = 0; + } + + // + // skip rest of header (type ID and name) and collect the + // number of struct members + // + if (!stream->skip_string () + || !stream->skip_string () + || !stream->get_ulong (members)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // iterate over all the members, skipping their names and + // looking only at type data. + // + for ( ; members != 0; members--) { + size_t member_size, member_alignment; + + // + // Skip name of the member. + // + if (!stream->skip_string ()) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Get size and alignment of the member, accounting for + // indirection and the various kinds of parameter encoding. + // + member_size = calc_nested_size_and_alignment (0, stream, + member_alignment, env); + if (env.exception () != 0) + return 0; + + // + // Round up the struct size to handle member alignment (by + // adding internal padding), then update the current size + // to handle the member's size. + // + size = (size_t) align_binary (size, member_alignment); + size += member_size; + + // + // Finally update the overall structure alignment requirement, + // if this element must be more strongly aligned. + // + if (member_alignment > alignment) + alignment = member_alignment; + }; + + // + // Round up the structure size to match its overall alignment. + // This adds tail padding, if needed. + // + return (size_t) align_binary (size, alignment); +} + + +// +// Calculate size and alignment for a structure. +// +static size_t +calc_struct_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +) +{ + return calc_struct_and_except_attributes (stream, + alignment, CORBA_B_FALSE, env); +} + + +// +// Calculate size and alignment for an exception. +// +static size_t +calc_exception_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +) +{ + return calc_struct_and_except_attributes (stream, + alignment, CORBA_B_TRUE, env); +} + + +// +// Calculate and return sizes for both parts of a union, as needed by +// other code. Return value is the overall size. The padded size of +// the discriminant is needed to traverse the two values separately. +// Unfortunately that is not quite practical to do with a single pass +// over the typecode: the inter-element padding changes depending on +// the strictest alignment required by _any_ arm of the union. +// +static size_t +calc_key_union_attributes ( + CDR *stream, + size_t &overall_alignment, + size_t &discrim_size_with_pad, + CORBA_Environment &env +) +{ + CORBA_ULong members; + CORBA_ULong temp; + size_t discrim_size; + size_t value_alignment, value_size; + + overall_alignment = value_alignment = 1; + value_size = discrim_size_with_pad = 0; + + // + // Skip initial optional members (type ID and name). + // + if (!stream->skip_string () // type ID + || !stream->skip_string ()) { // typedef name + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Calculate discriminant size and alignment: it's the first + // member of the "struct" representing the union. We detect + // illegal discriminant kinds a bit later. + // + CORBA_TypeCode discrim_tc (tk_void); + + discrim_size = calc_nested_size_and_alignment (&discrim_tc, + stream, overall_alignment, env); + if (env.exception () != 0) + return 0; + + // + // skip "default used" indicator, and save "member count" + // + if (!stream->get_ulong (temp) // default used + || !stream->get_ulong (members)) { // member count + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // iterate over the tuples for all the members; all we care about + // is their types, which can affect either alignment or padding + // requirement for the union part of the construct. + // + for ( ; members != 0; members--) { + size_t member_size, member_alignment; + + // + // Skip member label; its size varies with discriminant type, but + // here we don't care about its content. This is where illegal + // discriminant kinds are detected. + // + // NOTE: This modifies 94-9-32 Appendix A to stipulate that + // "long long" values are not legal as discriminants. + // + switch (discrim_tc._kind) { + case tk_short: + case tk_ushort: + case tk_wchar: + { + CORBA_Short s; + + if (!stream->get_short (s)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + case tk_long: + case tk_ulong: + case tk_enum: + { + CORBA_Long l; + + if (!stream->get_long (l)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + case tk_boolean: + case tk_char: + { + char c; + + if (!stream->get_byte (c)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + break; + + default: + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // We also don't care about any member name. + // + if (!stream->skip_string ()) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Get the member size and alignment. + // + member_size = calc_nested_size_and_alignment (0, stream, + member_alignment, env); + if (env.exception () != 0) + return 0; + + // + // Save the largest member and alignment. They don't need to + // be changed in sync -- e.g. "long double" size is larger + // than its alignment restriction on SPARC, x86, and some m68k + // platforms. + // + if (member_size > value_size) + value_size = member_size; + if (member_alignment > value_alignment) + value_alignment = member_alignment; + } + + // + // Round up the discriminator's size to include padding it needs + // in order to be followed by the value. + // + discrim_size_with_pad = (size_t) + align_binary (discrim_size, value_alignment); + + // + // Now calculate the overall size of the structure, which is the + // discriminator, inter-element padding, value, and tail padding. + // We know all of those except tail padding, which is a function + // of the overall alignment. (Ensures that arrays of these can + // be safely allocated and accessed!) + // + if (value_alignment > overall_alignment) + overall_alignment = value_alignment; + + return (size_t) align_binary (discrim_size_with_pad + value_size, + overall_alignment); +} + + +// +// Calculate size and alignment for a CORBA discriminated union. +// +// Note that this is really a two-element structure. The first element +// is the discriminator; the second is the value. All normal structure +// padding/alignment rules apply. In particular, all arms of the +// union have the same initial address (adequately aligned for any +// of the members). +// +static size_t +calc_union_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +) +{ + size_t scratch; + + return calc_key_union_attributes (stream, alignment, scratch, env); +} + + +// +// Calculate size and alignment for a typedeffed type. +// +static size_t +calc_alias_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +) +{ + // + // Skip type ID and name in the parameter stream + // + if (!stream->skip_string () // type ID + || !stream->skip_string () // typedef name + ) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // The typedef is identical to the type for which it stands. + // + return calc_nested_size_and_alignment (0, stream, alignment, env); +} + + +// +// Calculate size and alignment of an array. (All such arrays are +// described as single dimensional, even though the IDL definition +// may specify a multidimensional array ... such arrays are treated +// as nested single dimensional arrays.) +// +static size_t +calc_array_attributes ( + CDR *stream, + size_t &alignment, + CORBA_Environment &env +) +{ + size_t member_size; + CORBA_ULong member_count; + + // + // get size and alignment of the array member + // + member_size = calc_nested_size_and_alignment (0, stream, alignment, env); + if (env.exception () != 0) + return 0; + + // + // Get and check count of members. + // + if (stream->get_ulong (member_count) == CORBA_B_FALSE + || member_count > UINT_MAX) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // Array size is a function only of member number and count + // + return member_size * (size_t) member_count; +} + + +// +// Visit each of the elements of a structure. +// +static +CORBA_TypeCode::traverse_status +struct_traverse ( + CDR *stream, + const void *value1, + const void *value2, + CORBA_TypeCode::traverse_status (_FAR *visit) ( + CORBA_TypeCode_ptr tc, + const void *value1, + const void *value2, + void *context, + CORBA_Environment &env + ), + void *context, + CORBA_Environment &env +) +{ + // + // Skip over the type ID and type name in the parameters, then + // get the number of members. + // + CORBA_ULong members; + + if (!stream->skip_string () // type ID + || !stream->skip_string () // type name + || !stream->get_ulong (members)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // Visit each member of the structure/exception. The initial + // pointer(s) point at the first values to visit. For structs + // we could avoid the inter-member padding ... not for the + // case of exceptions. No big deal. + // + // NOTE: For last element, could turn visit() call into something + // subject to compiler's tail call optimization and thus save + // a stack frame. + // + CORBA_TypeCode::traverse_status retval; + + for (retval = CORBA_TypeCode::TRAVERSE_CONTINUE; + members != 0 && retval == CORBA_TypeCode::TRAVERSE_CONTINUE; + members--) { + CORBA_TypeCode member_tc (tk_null); + size_t size, alignment; + + // + // Skip the member's name in the parameter list. + // + if (!stream->skip_string ()) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // Get the member's size, alignment, and a temporary TypeCode, + // skipping that TypeCode in the stream as we do so. + // + // This accounts for all variations: different or nonexistent + // parameter lists, errors such as out-of-range TCKind values + // or nested exceptions, and indirected typecodes. + // + size = calc_nested_size_and_alignment (&member_tc, stream, + alignment, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Pad the value pointers to account for the alignment requirements + // of this member, then visit. + // + value1 = ptr_align_binary ((const unsigned char *) value1, alignment); + value2 = ptr_align_binary ((const unsigned char *) value2, alignment); + + retval = visit (&member_tc, value1, value2, context, env); + + // + // Update 'value' pointers to account for the size of the + // values just visited. + // + value1 = size + (char *)value1; + value2 = size + (char *)value2; + + if (env.exception () != 0) + retval = CORBA_TypeCode::TRAVERSE_STOP; + } + + return retval; +} + + +// +// cast the discriminant values to the right type and compare them. +// +static CORBA_Boolean +match_value ( + CORBA_TCKind kind, + CDR *tc_stream, + const void *value, + CORBA_Environment &env +) +{ + CORBA_Boolean retval = CORBA_B_FALSE; + + switch (kind) { + case tk_short: + case tk_ushort: + { + CORBA_UShort discrim; + + if (tc_stream->get_ushort (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(CORBA_UShort *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + case tk_long: + case tk_ulong: + { + CORBA_ULong discrim; + + if (tc_stream->get_ulong (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(CORBA_ULong *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + case tk_enum: + { + CORBA_ULong discrim; + + if (tc_stream->get_ulong (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(unsigned *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + case tk_boolean: + { + CORBA_Boolean discrim; + + if (tc_stream->get_boolean (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(CORBA_Boolean *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + case tk_char: + { + CORBA_Char discrim; + + if (tc_stream->get_char (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(CORBA_Char *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + case tk_wchar: + { + CORBA_WChar discrim; + + if (tc_stream->get_wchar (discrim) != CORBA_B_FALSE) { + retval = (discrim == *(CORBA_WChar *)value); + } else { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + } + break; + + + default: + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + } + + return retval; +} + +// +// Visit the two elements of the union: the discrminant, and then any +// specific value as indicated by the discriminant of value1. +// +static +CORBA_TypeCode::traverse_status +union_traverse ( + CDR *stream, + const void *value1, + const void *value2, + CORBA_TypeCode::traverse_status + (_FAR *visit) ( + CORBA_TypeCode_ptr tc, + const void *value1, + const void *value2, + void *context, + CORBA_Environment &env + ), + void *context, + CORBA_Environment &env +) +{ + size_t discrim_size_with_pad; + + // + // Figure out size of discriminant plus padding, used to adjust value + // pointers later. This can't be calculated without looking at all + // branches of the union ... forcing union traversal to be a two-pass + // algorithm, unless/until some data gets squirreled away. + // + { + CDR temp_cdr; + size_t scratch; + + temp_cdr.next = stream->next; + temp_cdr.remaining = stream->remaining; + temp_cdr.do_byteswap = stream->do_byteswap; + temp_cdr.do_free = 0; + + (void) calc_key_union_attributes (&temp_cdr, scratch, + discrim_size_with_pad, env); + } + if (env.exception() != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Skip the optional type ID and type name. + // + if (!stream->skip_string () // type ID, hidden + || !stream->skip_string ()) { // typedef name + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // Get and skip the discriminant's TypeCode. This allow for + // indirection (e.g. a complex enum discriminant). We use + // that TypeCode to visit the discriminant. + // + // We know the kind is legal and the TypeCode is valid because + // this repeats work we did earlier -- so checks are omitted. + // + CORBA_TypeCode discrim_tc (tk_null); + + { + size_t scratch; + + (void) calc_nested_size_and_alignment (&discrim_tc, + stream, scratch, env); + } + + if (visit (&discrim_tc, value1, value2, context, env) + == CORBA_TypeCode::TRAVERSE_STOP) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Adjust the pointers to point to the other member of the + // union; this ensures alignment for any of the values. + // Save the pointer to the discriminant though; we need it + // to find out which member to visit! + // + const void *discrim_ptr = value1; + + value1 = discrim_size_with_pad + (char *) value1; + value2 = discrim_size_with_pad + (char *) value2; + + // + // Get the flag that tells if there's a "default" arm in this + // union, then the number of members in the union. + // + CORBA_Long default_used = 0; + CORBA_ULong member_count; + + if (!stream->get_long (default_used)) { // default used + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + if (!stream->get_ulong (member_count)) { // member count + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // Scan to find the tuple whose value matches the discriminator. + // + // While we're scanning, record any default arm's information. + // If we can't find a match for the discriminant value, that arm + // will be used later. + // + unsigned char *default_tc_ptr = 0; + size_t default_tc_len; + + while (member_count-- != 0) { + // + // Test to see if the discriminant value matches the one in + // the TypeCode; this skips the the discriminant value in + // this CDR stream. + // + CORBA_Boolean discrim_matched; + + discrim_matched = match_value (discrim_tc._kind, + stream, discrim_ptr, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Skip the name of the member; we never care about it. + // + if (!stream->skip_string ()) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // If this is the default member, remember where its + // typecode data is stored; we'll use it later. + // + if (default_used >= 0 && default_used-- == 0) { + default_tc_ptr = stream->next; + default_tc_len = stream->remaining; + } + + // + // Get the TypeCode for this member. + // + // XXX we really don't care about size and alignment this time, + // only that we initialize the TypeCode. + // + CORBA_TypeCode tc (tk_null); + size_t scratch; + + (void) calc_nested_size_and_alignment (&tc, stream, scratch, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // If we matched, visit the member and return. + // + if (discrim_matched) + return visit (&tc, value1, value2, context, env); + } + + // + // If we get here, it means any default arm should be used. We + // know at least the basic sanity checks passed; we don't repeat. + // + if (default_tc_ptr) { + CDR temp_str; + size_t scratch; + CORBA_TypeCode tc (tk_null); + + temp_str.next = default_tc_ptr; + temp_str.remaining = default_tc_len; + temp_str.do_byteswap = stream->do_byteswap; + + // + // Get and use the TypeCode. + // + // XXX we really don't care about size and alignment this time, + // only that we initialize the TypeCode. + // + (void) calc_nested_size_and_alignment (&tc, &temp_str, scratch, env); + return visit (&tc, value1, value2, context, env); + } + return CORBA_TypeCode::TRAVERSE_CONTINUE; +} + + +// +// For each node in "data", visit it. For singleton nodes that's all but +// a NOP; for structs, unions, etc it's more interesting. The visit routine +// can descend, if it chooses. +// +// NOTE: this does no memory allocation or deallocation except through use +// of the stack. Or at least, it should do none -- if you find that just +// traversing a data value allocates any memory, that's a bug to fix! +// +CORBA_TypeCode::traverse_status +CORBA_TypeCode::traverse ( + const void *value1, + const void *value2, + CORBA_TypeCode::traverse_status (_FAR *visit) ( + CORBA_TypeCode_ptr tc, + const void *value1, + const void *value2, + void *context, + CORBA_Environment &env + ), + void *context, + CORBA_Environment &env +) +{ + env.clear (); + + // + // Quickly accomodate the bulk of cases, which are just (tail) calls + // to the visit() routine. We take advantage of the fact that these + // are largely in a convenient numeric range to work around poor + // optimization of "switch" code in some compilers. This improvement + // has in some cases been more than 5% faster (significant). + // + // NOTE: if for some reason the constants in the protocol spec + // (including Appendix A) change, this logic may need to be verified + // again. Luckily, changing protocol constants is quite rare; they + // normally just get added to (at the end). + // + if (_kind <= tk_objref + || (tk_longlong <= _kind && _kind <= tk_wstring)) + return visit (this, value1, value2, context, env); + + // + // Handle the cases that aren't in convenient numeric ranges. + // + traverse_status retval; + + switch (_kind) { + case tk_string: + case tk_enum: + return visit (this, value1, value2, context, env); + + // + // Typedefs just add a delay, while we skip the typedef ID + // and name ... + // + case tk_alias: + { + CORBA_TypeCode_ptr tcp; + CORBA_Environment env2; + + // + // XXX rework for efficiency, this doesn't need to + // allocate memory during the traversal! + // + tcp = typecode_param (2, env); + if (env.exception () != 0) + return TRAVERSE_STOP; + + retval = tcp->traverse (value1, value2, visit, context, env); + + tcp->Release (); + } + return retval; + + // + // Exceptions in-memory are structures, except that there are data + // members "hidden" in front: vtable, typecode, refcount. We skip + // them, and allow the traversal code to account for the internal + // padding before the other elements of the exception. + // + // NOTE: see header comment re treatment of these values as "real" + // C++ exceptions. C++ RTTI data might need to be skipped. Also, + // see the comments in unmarshaling code: hard to throw these values. + // + // Not enough of the exception runtime is public for binary standards + // to exist for C++ exceptions yet. Compiler-specific code will need + // to handle examining, unmarshaling, and throwing of CORBA exceptions + // (in C++ environments) for some time. + // + case tk_except: + value1 = sizeof (CORBA_Exception) + (char *) value1; + value2 = sizeof (CORBA_Exception) + (char *) value2; + // FALLTHROUGH + + case tk_struct: + // + // XXX for OLE Automation, we'll probably need BOTH exceptions + // and structs to inherit IUnknown, hence we'll need to be + // skipping the vtable pointer ... + // + { + // + // Create the sub-encapsulation stream that holds the + // parameters for the typecode. + // + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + return struct_traverse (&stream, value1, value2, + visit, context, env); + } + + case tk_union: + { + // + // visit the discriminant, then search the typecode for the + // relevant union member and then visit that member. + // + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + return union_traverse (&stream, value1, value2, + visit, context, env); + } + + // + // Sequences are just arrays with bound determined at runtime, rather + // than compile time. Multidimensional arrays are nested C-style: + // the leftmost dimension in the IDL definition is "outermost", etc. + // + { + CORBA_TypeCode_ptr tc2; + size_t size; + CORBA_ULong bounds; + CORBA_OctetSeq *seq; + + case tk_sequence: + // + // Find out how many elements there are, and adjust the + // data pointers to point to those elements rather than + // to the sequence itself. + // + seq = (CORBA_OctetSeq *)value1; + + bounds = seq->length; + value1 = seq->buffer; + + if (value2) { + seq = (CORBA_OctetSeq *)value2; + value2 = seq->buffer; + } + goto shared_seq_array_code; + + case tk_array: + // + // Array bounds are in the typecode itself. + // + bounds = ulong_param (1, env); + if (env.exception () != 0) + return TRAVERSE_STOP; + + shared_seq_array_code: + // + // Find element's type, and size ... + // + tc2 = typecode_param (0, env); + if (env.exception () != 0) + return TRAVERSE_STOP; + + size = tc2->size (env); + if (env.exception () != 0) + return TRAVERSE_STOP; + + // + // ... then visit the elements in order. + // + // NOTE: for last element, could turn visit() call into something + // subject to compiler's tail call optimization and thus save + // a stack frame + // + while (bounds--) { + if (visit (tc2, value1, value2, context, env) == TRAVERSE_STOP) + return TRAVERSE_STOP; + + value1 = size + (char *) value1; + value2 = size + (char *) value2; + } + CORBA_release (tc2); + env.clear (); + } + return TRAVERSE_CONTINUE; + + // case ~0: // indirection, illegal at top level + default: // invalid/illegal + break; + + } // end switch on typecode "kind" + + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return TRAVERSE_STOP; +} + + +// +// Tell user the size of an instance of the data type described by this +// typecode ... typically used to allocate memory. +// +size_t +CORBA_TypeCode::size (CORBA_Environment &env) +{ + if (_kind >= TC_KIND_COUNT) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + env.clear (); + + if (table [_kind].calc == 0) + return table [_kind].size; + + size_t alignment; + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + return table [_kind].calc (&stream, alignment, env); +} + + +// +// Tell user the alignment restriction for the data type described by an +// instance of this data type. Rarely used; provided for completeness. +// +size_t +CORBA_TypeCode::alignment (CORBA_Environment &env) +{ + if (_kind >= TC_KIND_COUNT) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + env.clear (); + + if (table [_kind].calc == 0) + return table [_kind].alignment; + + size_t alignment; + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t) _length); + + (void) table [_kind].calc (&stream, alignment, env); + return alignment; +} diff --git a/TAO/IIOP/lib/runtime/marshal.cpp b/TAO/IIOP/lib/runtime/marshal.cpp new file mode 100644 index 00000000000..60531602eb8 --- /dev/null +++ b/TAO/IIOP/lib/runtime/marshal.cpp @@ -0,0 +1,1101 @@ +// @(#)marshal.cpp 1.7 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// CDR: Marshaling interpreter +// +// This marshaling interpreter is driven by the typecode interpreter. The +// typecode interpreter understands each compiler environment's rules for +// data structure layout; this understands CDR's rules for on-the-wire data +// structure layout. +// +// Apart from some high level rules related to construction of complex data +// types, the marshaling interpreter just knows how to encode primitive data +// types and allocate memory on decode. +// +// NOTE: to reduce the amount of compiled code, this "knows" facts like +// native float/long/ulong being in legal CDR format, and that "char" is +// native in ISO Latin/1 (so no transformation is needed, and octet/char +// marshaling code is identical). On exotic platforms where this is not +// true, some of the merged "switch" branches will need to be split. +// +// REMEMBER: goal is to have the typecode interpreter plus one side of the +// marshaling interpreter reside in a CPU's code cache; or at least to have +// as litle as possible _outside_ cache when marshaling. Compiled marshaling +// code will have a few less instructions, but most will of them will be +// outside the instruction cache; access time to get at them will be high. +// +// NOTE: One interesting optimization is inlining the primitive put/get +// calls ... it'd typically save at least 40% in terms of instruction count +// on each of these critical paths by eliminating subroutine call overhead. +// Since it would increase code size, such changes might not be desirable +// on machines with small caches. Also, with network I/O being today's most +// significant bottleneck, such optimizations haven't been well explored. +// +// THREADING NOTE: The only threading concern is as always, that data +// structures being manipulated by any given thread must be reserved to +// it by some mechanism (e.g. mutex). This uses no mutable data outside +// of the thread stack, so the onus is entirely on the caller. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/debug.hh" +#include "runtime/cdr.hh" + +#include "bridge/giop.hh" + + +#if defined (HAVE_WIDEC_H) +# include <widec.h> +#else + extern "C" { + unsigned wslen (const wchar_t *); + wchar_t *wscpy (wchar_t *, const wchar_t *); + } +#endif + +extern CORBA_TypeCode TC_opaque; + + +// +// Encode instances of arbitrary data types based only on typecode. "data" +// points to the data type; if it's not a primitve data type, the TypeCode +// interpreter is used to recursively encode its components. "context" is +// the marshaling stream on which to encode the data value. +// +// This is a fairly typical TypeCode interpreter visit() routine; it works +// on a single data value in conjunction with context information, and must +// handle all IDL data types. +// +CORBA_TypeCode::traverse_status +CDR::encoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *, + void *context, + CORBA_Environment &env +) +{ + CORBA_Boolean continue_encoding = CORBA_B_TRUE; + CDR *stream = (CDR *)context; + + switch (tc->_kind) { + case tk_null: + case tk_void: + // nothing to encode! + break; + + case tk_char: + case tk_octet: + continue_encoding = stream->put_char (*(char *)data); + break; + + case tk_short: + case tk_ushort: + continue_encoding = stream->put_short (*(short *)data); + break; + + case tk_long: + case tk_ulong: + case tk_float: + continue_encoding = stream->put_long (*(CORBA_Long *)data); + break; + + case tk_double: + case tk_longlong: + case tk_ulonglong: + continue_encoding = stream->put_longlong (*(CORBA_LongLong *)data); + break; + + case tk_boolean: + continue_encoding = stream->put_boolean (*(CORBA_Boolean *)data); + break; + + case tk_enum: + { + // + // NOTE assumption that this is in-range. + // + // XXX should check this, it's a hard-to-recover error + // for the other side + // + unsigned value = *(unsigned *)data; + continue_encoding = stream->put_ulong (value); + } + break; + + case tk_any: + { + CORBA_Any *any = (CORBA_Any *)data; + + tc = any->type (); + if (encoder (_tc_CORBA_TypeCode, &tc, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return CORBA_TypeCode::TRAVERSE_STOP; + + data = any->value (); + return encoder (tc, data, 0, context, env); + } + // NOTREACHED + + case tk_TypeCode: + { + CORBA_TypeCode_ptr tc2; + + tc2 = *(CORBA_TypeCode_ptr *)data; + + continue_encoding = stream->put_ulong ((CORBA_ULong) tc2->_kind); + if (continue_encoding == CORBA_B_FALSE) + break; + + switch (tc2->_kind) { + // + // Most TypeCodes have empty parameter lists + // + default: + break; + + // + // A few have "simple" parameter lists + // + case tk_string: + case tk_wstring: + continue_encoding = stream->put_ulong (tc2->_length); + break; + + // + // Indirected typecodes can't occur at "top level" like + // this, only nested inside others! + // + case ~0: + dmsg ("indirected typecode at top level!"); + continue_encoding = CORBA_B_FALSE; + break; + + // + // The rest have "complex" parameter lists that are already + // encoded as bulk octets ... put length, then octets + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_sequence: + case tk_array: + case tk_alias: + case tk_except: + { + unsigned i; + + continue_encoding = stream->put_ulong (tc2->_length); + for (i = 0; i < tc2->_length && continue_encoding; i++) + continue_encoding = + stream->put_octet (tc2->_buffer [i]); + } + } + } + break; + + case tk_Principal: + { + CORBA_Principal_ptr p = *(CORBA_Principal_ptr*) data; + unsigned i; + + if (p != 0) { + continue_encoding = stream->put_long (p->id.length); + for (i = 0; continue_encoding && i < p->id.length; i++) + continue_encoding = stream->put_octet (p->id.buffer [i]); + } else + continue_encoding = stream->put_long (0); + } + break; + + case tk_objref: + // + // Current version: objref is really an IIOP_Object. + // + // This will change in the future; STUB_Object knows how to + // marshal itself, that will be used. + // + // XXX this doesn't actually verify that the stuff got written + // OK to the "wire" ... + // + { + CORBA_Object_ptr obj = *(CORBA_Object_ptr *)data; + + // + // NIL objrefs ... marshal as empty type hint, no elements. + // + if (CORBA_is_nil (obj)) { + continue_encoding = + stream->put_ulong (1) // strlen + && stream->put_char (0) // NUL + && stream->put_ulong (0); // no profiles + break; + } + + // + // All other objrefs ... narrow to a "real type" that we + // recognize, then marshal. + // + // XXX this will be changed so it narrows to STUB_Object + // and then asks that surrogate/proxy to marshal itself. + // + // For now, the original code is minimally changed. + // + IIOP_Object *objdata; + IIOP::ProfileBody *profile; + + if (obj->QueryInterface (IID_IIOP_Object, (void **)&objdata) + != NOERROR) { + env.exception (new CORBA_MARSHAL (COMPLETED_NO)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + obj->Release (); + profile = &objdata->profile; + + // + // STRING, a type ID hint + // + encoder (_tc_CORBA_String, &objdata->type_id, 0, context, env); + + // + // UNSIGNED LONG, value one, count of the sequence + // of encapsulated protocol profiles; + // + stream->put_ulong (1); + + // + // UNSIGNED LONG, tag for this protocol profile; + // + stream->put_ulong (IOP::TAG_INTERNET_IOP); + + // + // UNSIGNED LONG, number of succeeding bytes in the encapsulation. + // We don't actually need to make the encapsulation, as nothing + // needs stronger alignment than this longword; it guarantees + // the rest is aligned for us. + // + unsigned hostlen; + + hostlen = strlen ((char *)profile->host); + stream->put_ulong ( + 1 // byte order + + 3 // version + pad byte + + 4 // sizeof (strlen) + + hostlen + 1 // strlen + null + + (~hostlen & 01) // optional pad byte + + 2 // port + + (hostlen & 02) // optional pad short + + 4 // sizeof (key length) + + profile->object_key.length); // key length + + // + // CHAR describing byte order, starting the encapsulation + // + stream->put_char (MY_BYTE_SEX); + + // + // IIOP::Version, two characters (version 1.0) + // padding + // + stream->put_char (profile->iiop_version.major); + stream->put_char (profile->iiop_version.minor); + + // + // STRING hostname from profile + // + encoder (_tc_CORBA_String, &profile->host, 0, context, env); + + // + // UNSIGNED SHORT port number + // + stream->put_ushort (profile->port); + + // + // OCTET SEQUENCE for object key + // + encoder (&TC_opaque, &profile->object_key, 0, context, env); + } + break; + + case tk_sequence: + { + // + // First marshal the sequence length, verifying that + // it's within the sequence bounds ... + // + CORBA_OctetSeq *seq = (CORBA_OctetSeq *) data; + CORBA_ULong len = seq ? seq->length : 0; + + if (len > 0) { + CORBA_ULong bounds; + + bounds = tc->ulong_param (1, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + + if (bounds != 0 && len > bounds) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_MAYBE)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + } + continue_encoding = stream->put_ulong (len); + + // + // Fast exit on error or empty sequence + // + if (!continue_encoding || len == 0) + break; + } + // FALLTHROUGH + + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + // + // Marshal each member in order. + // + return tc->traverse (data, 0, encoder, context, env); + + case tk_except: + // + // Convert the the "hidden" TypeCode at the beginning of the + // exception into an on-the-wire ID, then marshal the members + // in order (traversal skips that hidden typecode, and more). + // + // NOTE: This is asymmetric with respect to decoding the exception, + // since whoever decodes must pull off the ID and map it to the + // typecode to be used to unmarshal it (search among legal choices). + // + { + CORBA_String id = tc->id (env); + + if (env.exception () == 0) { + continue_encoding = + encoder (_tc_CORBA_String, &id, 0, context, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE + && tc->traverse (data, 0, encoder, context, env); + } else + continue_encoding = CORBA_B_FALSE; + } + break; + + case tk_string: + { + CORBA_String str = *(CORBA_String *) data; + CORBA_ULong len, bounds; + + // + // Be nice to programmers: treat nulls as empty strings + // not errors. (OMG-IDL supports languages that don't use + // the C/C++ notion of null v. empty strings; nulls aren't + // part of the OMG-IDL string model.) + // + if (str == 0) { + stream->put_ulong (1); + stream->put_char (0); + break; + } + + // + // Verify string satisfies bounds requirements. We're not + // so permissive as to send messages violating the interface + // spec by having excessively long strings! + // + bounds = tc->ulong_param (0, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + len = strlen ((char *)str); + + if (bounds != 0 && len > bounds) { + continue_encoding = CORBA_B_FALSE; + break; + } + + // + // Encode the string, followed by a NUL character. + // + continue_encoding = stream->put_ulong (len + 1); + while (continue_encoding != CORBA_B_FALSE && *str) + continue_encoding = stream->put_char (*str++); + stream->put_char (0); + } + break; + + case tk_wstring: + { + wchar_t *str = *(wchar_t **) data; + CORBA_ULong len, bounds; + + // + // Be nice to programmers: treat nulls as empty strings + // not errors. (OMG-IDL supports languages that don't use + // the C/C++ notion of null v. empty strings; nulls aren't + // part of the OMG-IDL string model.) + // + if (str == 0) { + stream->put_ulong (1); + stream->put_wchar (0); + break; + } + + // + // Verify wide string satisfies bounds requirements. We're + // not so permissive as to send messages violating the interface + // spec by having excessively long strings! + // + bounds = tc->ulong_param (0, env); + if (env.exception () != 0) + return CORBA_TypeCode::TRAVERSE_STOP; + len = wslen (str); + if (bounds != 0 && len > bounds) { + continue_encoding = CORBA_B_FALSE; + break; + } + + // + // Encode the wide string, followed by a NUL character. + // + continue_encoding = stream->put_ulong (wslen (str) + 1); + while (continue_encoding != CORBA_B_FALSE && *str) + continue_encoding = stream->put_wchar (*str++); + stream->put_wchar (0); + } + break; + + case tk_longdouble: + continue_encoding = + stream->put_longdouble (*(CORBA_LongDouble *)data); + break; + + case tk_wchar: + continue_encoding = stream->put_wchar (*(wchar_t *)data); + break; + + // case ~0: + default: + dmsg ("encoder default case ?"); + continue_encoding = CORBA_B_FALSE; + break; + } + + if (continue_encoding == CORBA_B_FALSE) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); + dmsg ("marshaling encoder detected error"); + return CORBA_TypeCode::TRAVERSE_STOP; + } + return CORBA_TypeCode::TRAVERSE_CONTINUE; +} + + +// +// This table of TypeCode constants lets us unmarshal most typecodes using +// the predefined constants, rather than constantly reallocating them. +// +// XXX CFRONT-based compilers can't cope with this table initialization, +// and need some kind of init function. Luckily, they're increasingly +// rare in any "production" environment. +// +const CORBA_TypeCode_ptr +__tc_consts [TC_KIND_COUNT] = { + _tc_CORBA_Null + , _tc_CORBA_Void + , _tc_CORBA_Short + , _tc_CORBA_Long + , _tc_CORBA_UShort + + , _tc_CORBA_ULong + , _tc_CORBA_Float + , _tc_CORBA_Double + , _tc_CORBA_Boolean + , _tc_CORBA_Char + + , _tc_CORBA_Octet + , _tc_CORBA_Any + , _tc_CORBA_TypeCode + , _tc_CORBA_Principal + , 0 // _tc_CORBA_Object ... type ID is CORBA::Object + + , 0 // tk_struct + , 0 // tk_union + , 0 // tk_enum + , 0 // _tc_CORBA_String ... unbounded + , 0 // tk_sequence + + , 0 // tk_array + + , 0 // tk_alias + , 0 // tk_except + + , _tc_CORBA_LongLong + , _tc_CORBA_ULongLong + , _tc_CORBA_LongDouble + , _tc_CORBA_WChar + , 0 // _tc_CORBA_WString ... unbounded +}; + + +// +// The decoder is exactly the reverse of the encoder, except that: +// +// * Unmarshaling some data types involve allocating memory. Such +// types include sequences (the buffer), objrefs, Principals, Anys, +// TypeCodes, and strings. +// +// * The decoder is used when retrieving typecode parameters from +// encapsulations. This means it must deal with "tk_indirect", +// the magic value (~0) signifying typecode indirection. +// +// This second case is identified by a bit of a hack: the second "data" +// value is used to hold the parent typecode, rather than being ignored. +// This means that all other invocations of decoder() ** MUST ** pass zero +// for the second data parameter, in case they decode a TypeCode. If they +// didn't, this case might be signified inappropriately. +// +// XXX desirable to have a less hacky solution to that ... pull that code +// out into a separate routine called both by CDR::decoder() and by the +// code retrieving typecode parameters from encapsulations. +// +CORBA_TypeCode::traverse_status +CDR::decoder ( + CORBA_TypeCode_ptr tc, + const void *data, + const void *parent_typecode, + void *context, + CORBA_Environment &env +) +{ + CORBA_Boolean continue_decoding = CORBA_B_TRUE; + CDR *stream = (CDR *)context; + + switch (tc->_kind) { + case tk_null: + case tk_void: + // nothing to decode! + break; + + case tk_char: + case tk_octet: + continue_decoding = stream->get_char (*(CORBA_Char *)data); + break; + + case tk_short: + case tk_ushort: + continue_decoding = stream->get_short (*(short *)data); + break; + + case tk_long: + case tk_ulong: + case tk_float: + continue_decoding = stream->get_long (*(CORBA_Long *)data); + break; + + case tk_longlong: + case tk_ulonglong: + case tk_double: + continue_decoding = stream->get_longlong (*(CORBA_LongLong *)data); + break; + + case tk_boolean: + continue_decoding = stream->get_boolean (*(CORBA_Boolean *)data); + break; + + case tk_enum: + { + CORBA_ULong val; + + // + // NOTE assumption that this is in-range. + // + // XXX should check this, it's rather hard to recover + // from such errors since they "do not occur" and are + // essentially never tested for. + // + continue_decoding = stream->get_ulong (val); + *(unsigned *)data = (unsigned) val; + } + break; + + case tk_any: + { + CORBA_Any *any = (CORBA_Any *)data; + CORBA_TypeCode_ptr tc2; + void *value; + + if (decoder (_tc_CORBA_TypeCode, &tc2, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) + return CORBA_TypeCode::TRAVERSE_STOP; + + value = new CORBA_Octet [tc2->size (env)]; + + if (decoder (tc2, value, 0, context, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + delete value; + CORBA_release (tc2); + return CORBA_TypeCode::TRAVERSE_STOP; + } + (void) new (any) CORBA_Any (tc2, value, CORBA_B_TRUE); + } + break; + + case tk_TypeCode: + { + CORBA_ULong kind; + CORBA_TypeCode_ptr *tcp; + + continue_decoding = stream->get_ulong (kind); + if (continue_decoding == CORBA_B_FALSE) + break; + if (kind >= TC_KIND_COUNT) { + continue_decoding = CORBA_B_FALSE; + break; + } + + tcp = (CORBA_TypeCode_ptr *)data; + + // + // Typecodes with empty parameter lists all have preallocated + // constants. We use those to reduce memory consumption and + // heap access ... also, to speed things up! + // + if (((*tcp) = __tc_consts [(unsigned) kind]) != 0) { + *tcp = __tc_consts [(unsigned) kind]; + break; + } else switch (kind) { + // + // Need special handling for all kinds of typecodes that have + // nonempty parameter lists ... + // + default: // error: missed a case! + env.exception (new CORBA_INTERNAL (COMPLETED_MAYBE)); + return CORBA_TypeCode::TRAVERSE_STOP; + + // + // Some have "simple" parameter lists ... some of these also + // have preallocated constants that could be used. + // + case tk_string: + case tk_wstring: + { + CORBA_ULong bound; + + continue_decoding = stream->get_ulong (bound); + if (continue_decoding) { + if (bound == 0) { + if (kind == tk_string) + *tcp = _tc_CORBA_String; + else + *tcp = _tc_CORBA_WString; + } else { + *tcp = new CORBA_TypeCode ((CORBA_TCKind) kind, + bound, 0, CORBA_B_TRUE); + } + } + } + break; + + // + // Indirected typecodes, illegal at "top level" but we allow + // unmarshaling of them here because we use the same code to + // read "off the wire" (where they're illegal) and to read + // out of an encapsulation stream. We distinguish the case + // where this is legal as described above. + // + case ~0: + { + CORBA_TypeCode_ptr parent; + + if (parent_typecode == 0) { + env.exception (new CORBA_INTERNAL (COMPLETED_MAYBE)); + return CORBA_TypeCode::TRAVERSE_STOP; + } + parent = (CORBA_TypeCode_ptr) parent_typecode; + + // + // Get the long indicating the encapsulation offset, + // then set up indirection stream that's like "stream" + // but has space enough only for the typecode and + // the length for the encapsulated parameters. + // + CDR indir_stream; + CORBA_Long offset; + + continue_decoding = stream->get_long (offset); + if (continue_decoding) + continue_decoding = (offset < 0); + if (continue_decoding) { + indir_stream.buffer = indir_stream.next + = stream->next + offset; + indir_stream.remaining = indir_stream.length = 8; + + // + // Reject indirections outside parent's scope. + // + if (indir_stream.next < parent->_buffer) + continue_decoding = CORBA_B_FALSE; + } + + // + // Get "kind" and length of target typecode + // + // XXX this currently assumes the TCKind to which + // we indirect is the same byte order as the "parent" + // typecode -- not the right assumption; see how + // the TypeCode interpreter does it. + // + CORBA_ULong indir_kind, indir_len; + + if (continue_decoding) + continue_decoding = stream->get_ulong (indir_kind); + if (continue_decoding + && indir_kind >= TC_KIND_COUNT) + continue_decoding = CORBA_B_FALSE; + if (continue_decoding) + continue_decoding = stream->get_ulong (indir_len); + + // + // Now construct indirected typecode. This shares the + // typecode octets with the "parent" typecode, increasing + // the amount of memory sharing and reducing the cost of + // getting typecodes. + // + if (continue_decoding) { + *tcp = new CORBA_TypeCode ( + (CORBA_TCKind) indir_kind, + indir_len, indir_stream.next, + CORBA_B_FALSE); + (*tcp)->_parent = parent; + parent->AddRef (); + } + } + break; + + // + // The rest have "complex" parameter lists that are + // encoded as bulk octets ... + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_sequence: + case tk_array: + case tk_alias: + case tk_except: + { + unsigned len, i; + CORBA_ULong length; + CORBA_Octet *buffer; + + continue_decoding = stream->get_ulong (length); + if (!continue_decoding) + break; + + // if length > MAXUNSIGNED, error ... + len = (unsigned) length; + + buffer = new CORBA_Octet [len]; + + for (i = 0; i < len && continue_decoding; i++) + continue_decoding = stream->get_octet (buffer [i]); + + if (!continue_decoding) { + delete buffer; + break; + } + *tcp = new CORBA_TypeCode ((CORBA_TCKind)kind, + len, buffer, CORBA_B_TRUE); + } + } + } + break; + + case tk_Principal: + { + CORBA_Principal_ptr *pp = (CORBA_Principal_ptr *)data; + CORBA_ULong len; + + continue_decoding = stream->get_ulong (len); + if (len == 0) + *pp = 0; + else { + *pp = new CORBA_Principal; + (*pp)->id.buffer = new CORBA_Octet [(size_t) len]; + (*pp)->id.maximum = (*pp)->id.length = len; + + for (unsigned i = 0; + continue_decoding != CORBA_B_FALSE && i < len; + i++) + continue_decoding = stream->get_octet ( + (*pp)->id.buffer [i]); + } + } + break; + + case tk_objref: + { + // + // First, read the type hint. + // + CORBA_String type_hint; + + decoder (_tc_CORBA_String, &type_hint, 0, context, env); + + // + // Read the profiles, discarding all until an IIOP profile comes by. + // Once we see an IIOP profile, ignore any further ones. + // + // XXX this will need to change someday to let different protocol + // code be accessed, not just IIOP. Protocol modules will be + // dynamically loaded from shared libraries via ORB_init(), and + // we just need to be able to access such preloaded libraries here + // as we unmarshal objrefs. + // + CORBA_ULong profiles; + IIOP_Object *objdata = 0; + + stream->get_ulong (profiles); + + // + // No profiles means a NIL objref. + // + if (profiles == 0) { + *(CORBA_Object_ptr *)data = CORBA_Object::_nil (); + delete type_hint; + break; + } + while (profiles-- != 0 && continue_decoding) { + CORBA_ULong tmp; + + stream->get_ulong (tmp); + if (tmp != IOP::TAG_INTERNET_IOP || objdata != 0) { + continue_decoding = stream->skip_string (); + continue; + } + + // + // OK, we've got an IIOP profile. It's going to be + // encapsulated ProfileData. Create a new decoding + // stream and context for it, and tell the "parent" + // stream that this data isn't part of it any more. + // + continue_decoding = stream->get_ulong (tmp); + assert (stream->remaining >= tmp); + + // + // Create the decoding stream from the encapsulation + // in the buffer, and skip the encapsulation. + // + CDR str; + + str.setup_encapsulation (stream->next, (size_t) tmp); + + stream->next += (unsigned) tmp; + stream->remaining -= (unsigned) tmp; + + objdata = new IIOP_Object (type_hint); + + IIOP::ProfileBody *profile = &objdata->profile; + + // + // Read and verify major, minor versions, ignoring + // IIOP profiles whose versions we don't understand. + // + // XXX this doesn't actually go back and skip the + // whole encapsulation... + // + if (!(str.get_octet (profile->iiop_version.major) + && profile->iiop_version.major == IIOP::MY_MAJOR + && str.get_octet (profile->iiop_version.minor) + && profile->iiop_version.minor <= IIOP::MY_MINOR)) { + dmsg2 ("detected new v%d.%d IIOP profile", + profile->iiop_version.major, + profile->iiop_version.minor); + objdata->type_id = 0; + objdata->Release (); + objdata = 0; + continue; + } + + // + // Get host and port + // + if (decoder (_tc_CORBA_String, &profile->host, 0, &str, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || !str.get_ushort (profile->port)) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); + dmsg ("error decoding IIOP host/port"); + objdata->Release (); + return CORBA_TypeCode::TRAVERSE_STOP; + } + + // + // ... and object key + // + continue_decoding = + (decoder (&TC_opaque, &profile->object_key, 0, &str, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE); + + if (str.remaining != 0) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); + dmsg ("extra data at end of IIOP profile data"); + objdata->Release (); + return CORBA_TypeCode::TRAVERSE_STOP; + } + } + if (objdata == 0) { + env.exception (new CORBA_MARSHAL (COMPLETED_MAYBE)); + dmsg2 ("no IIOP v%d.%d (or earlier) profile in IOR!", + IIOP::MY_MAJOR, IIOP::MY_MINOR); + return CORBA_TypeCode::TRAVERSE_STOP; + } else { + if (objdata->QueryInterface (IID_CORBA_Object, (void **) data) + != NOERROR) + continue_decoding = CORBA_B_FALSE; + objdata->Release (); + } + } + break; + + case tk_sequence: + { + // + // First unmarshal the sequence length ... we trust it + // to be right here, on the "be gracious in what you + // accept" principle. We don't generate illegal sequences + // (i.e. length > bounds). + // + CORBA_OctetSeq *seq = (CORBA_OctetSeq *) data; + + continue_decoding = stream->get_ulong (seq->length); + seq->maximum = seq->length; + seq->buffer = 0; + + // + // Fast exit on empty sequences or errors + // + if (!continue_decoding || seq->length == 0) + break; + + // + // ... then allocate the memory into which we'll unmarshal + // + CORBA_TypeCode_ptr tc2; + size_t size; + + tc2 = tc->typecode_param (0, env); + if (env.exception ()) + return CORBA_TypeCode::TRAVERSE_STOP; + + size = tc2->size (env); + if (env.exception ()) + return CORBA_TypeCode::TRAVERSE_STOP; + + tc2->Release (); + + seq->buffer = new CORBA_Octet [size * (size_t) seq->maximum]; + } + // FALLTHROUGH + + case tk_struct: + case tk_union: + case tk_array: + case tk_alias: + // + // Unmarshal all the individual elements using the per-member + // description held in the "parent" TypeCode. + // + + // FALLTHROUGH + + case tk_except: + // + // For exceptions, the "hidden" type ID near the front of the + // on-wire representation was previously unmarshaled and mapped + // to the "tc" typcode we're using to traverse the memory ... + // at the same time its vtable, refcount, and other state was + // established. + // + // NOTE: This is asymmetric with respect to encoding exceptions. + // + return tc->traverse (data, 0, decoder, context, env); + + case tk_string: + { + CORBA_String str; + CORBA_ULong len = 0; + + // + // On decode, omit the check against specified string bounds, + // and cope with illegal "zero length" strings (all lengths + // on the wire must include a NUL). + // + // This is on the principle of being gracious in what we accept; + // we don't generate messages that fail to comply with protocol + // specs, but we will accept them when it's clear how to do so. + // + continue_decoding = stream->get_ulong (len); + *((CORBA_String*)data) = str = new CORBA_Char [(size_t) (len)]; + if (len != 0) + while (continue_decoding != CORBA_B_FALSE && len-- != 0) { + continue_decoding = stream->get_char (*(CORBA_Char *)str); + str++; + } + break; + } + + case tk_wstring: + { + wchar_t *str; + CORBA_ULong len = 0; + + // + // On decode, omit the check against specified wstring bounds, + // and cope with illegal "zero length" strings (all lengths + // on the wire must include a NUL). + // + // This is on the principle of being gracious in what we accept; + // we don't generate messages that fail to comply with protocol + // specs, but we will accept them when it's clear how to do so. + // + continue_decoding = stream->get_ulong (len); + *((wchar_t **)data) = str = new wchar_t [(size_t) (len)]; + if (len != 0) { + while (continue_decoding != CORBA_B_FALSE && len--) { + continue_decoding = stream->get_wchar (*str); + str++; + } + } + } + break; + + case tk_longdouble: + continue_decoding = + stream->get_longdouble (*(CORBA_LongDouble *)data); + break; + + case tk_wchar: + continue_decoding = stream->get_wchar (*(wchar_t *)data); + break; + + // case ~0: + default: + continue_decoding = CORBA_B_FALSE; + dmsg ("decode, default case?"); + break; + } + + if (continue_decoding == CORBA_B_FALSE) { + env.exception (new CORBA_MARSHAL (COMPLETED_NO)); + dmsg ("marshaling decoder detected error"); + return CORBA_TypeCode::TRAVERSE_STOP; + } + return CORBA_TypeCode::TRAVERSE_CONTINUE; +} + diff --git a/TAO/IIOP/lib/runtime/nvlist.cpp b/TAO/IIOP/lib/runtime/nvlist.cpp new file mode 100644 index 00000000000..896dd7c098f --- /dev/null +++ b/TAO/IIOP/lib/runtime/nvlist.cpp @@ -0,0 +1,262 @@ +// @(#)nvlist.cpp 1.6 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Implementation of Named Value List +// +#include <assert.h> +#include <memory.h> +#include <corba/orb.hh> + +#include <initguid.h> + +#include "runtime/debug.hh" +#include "runtime/thread.hh" + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t nvlist_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + + +// +// COM's IUnknown support +// + +// {77420087-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_NamedValue, +0x77420087, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_NamedValue::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&nvlist_lock); +#endif + + return _refcount++; +} + +ULONG +__stdcall +CORBA_NamedValue::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&nvlist_lock); +#endif + + assert (this != 0); + + if (--_refcount != 0) + return _refcount; + + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_NamedValue::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_NamedValue == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + +// +// Reference counting for DII Request object +// +void +CORBA_release (CORBA_NamedValue_ptr nv) +{ + if (nv) + nv->Release (); +} + +CORBA_Boolean +CORBA_is_nil (CORBA_NamedValue_ptr nv) +{ + return (CORBA_Boolean)(nv == 0); +} + +CORBA_NamedValue::~CORBA_NamedValue () +{ + if (_name) + CORBA_string_free ((CORBA_String)_name); +} + + +// +// COM's IUnknown support +// + +// {77420088-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_NVList, +0x77420088, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_NVList::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&nvlist_lock); +#endif + + return _refcount++; +} + +ULONG +__stdcall +CORBA_NVList::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&nvlist_lock); +#endif + + assert (this != 0); + + if (--_refcount != 0) + return _refcount; + + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_NVList::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_NVList == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + +// +// Reference counting for DII Request object +// +void +CORBA_release (CORBA_NVList_ptr nvl) +{ + if (nvl) + nvl->Release (); +} + +CORBA_Boolean +CORBA_is_nil (CORBA_NVList_ptr nvl) +{ + return (CORBA_Boolean)(nvl == 0); +} + +CORBA_NVList::~CORBA_NVList () +{ + unsigned i; + + for (i = 0; i < _len; i++) + (&_values [i])->~CORBA_NamedValue (); + + if (_values) + free ((char *)_values); + _values = 0; + _len = _max = 0; +} + +CORBA_NamedValue_ptr +CORBA_NVList::add_value ( + const CORBA_Char *name, + const CORBA_Any &value, + CORBA_Flags flags, + CORBA_Environment &env +) +{ + env.clear (); + + if (!(flags & (CORBA_ARG_IN|CORBA_ARG_OUT|CORBA_ARG_INOUT))) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; + } + + // + // We track "_len" and "_max" like sequences do; mixing the + // "add_arg" and nvlist[i] style accessors produces undefined + // behaviour. + // + unsigned len = _len++; + + // + // Extend the array with an _initialized_ element ... relying on + // zeroed memory to be sufficiently initialized. + // + // XXX report malloc failures as errors -- how? + // + if (_values == 0) { + _values = (CORBA_NamedValue_ptr) + calloc (_len, sizeof (CORBA_NamedValue)); + _max = _len; + } else if (len >= _max) { + _values = (CORBA_NamedValue_ptr) realloc ((char *)_values, + sizeof (CORBA_NamedValue) * _len); + (void) memset (&_values [_max], 0, + sizeof (_values [_max]) * (_len - _max)); + _max = _len; + } + assert (_values != 0); + + _values [len]._flags = flags; + _values [len]._name = CORBA_string_copy (name); + + if (flags & CORBA_IN_COPY_VALUE) { + // + // IN_COPY_VALUE means that the parameter is not "borrowed" + // by the ORB, but rather that the ORB copies its value. + // + // Initialize the newly allocated memory using a copy + // constructor that places the new "Any" value at just + // the right place, and makes a "deep copy" of the data. + // + (void) new (&_values [len]._any) CORBA_Any (value); + } else { + // + // The normal behaviour for parameters is that the ORB + // "borrows" their memory for the duration of calls. + // + // Initialize the newly allocated "Any" using a normal + // constructor that places the new "Any" value at just + // the right place, yet doesn't copy the memory (except + // for duplicating the typecode). + // + // NOTE: DSI has yet to be updated so that it's OK to + // use such application-allocated memory. It needs at + // least a "send the response now" call. + // + (void) new (&_values [len]._any) CORBA_Any (value.type (), + value.value (), CORBA_B_FALSE); + } + return &_values [len]; +} diff --git a/TAO/IIOP/lib/runtime/object.cpp b/TAO/IIOP/lib/runtime/object.cpp new file mode 100644 index 00000000000..e496546fc65 --- /dev/null +++ b/TAO/IIOP/lib/runtime/object.cpp @@ -0,0 +1,405 @@ +// @(#)object.cpp 1.9 95/09/19 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: CORBA::Object operations +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include <corba/stub.hh> +#include "runtime/debug.hh" + +#include <initguid.h> + +// +// Constructor and destructor are accessible to subclasses +// +CORBA_Object::CORBA_Object (IUnknown *_jan) +: parent (_jan) +{ + assert (parent != 0); +} + +CORBA_Object::~CORBA_Object () +{ +} + +// +// CORBA dup/release build on top of COM's (why not). +// +void +CORBA_release ( + CORBA_Object_ptr obj +) +{ + if (obj) + obj->Release (); +} + +CORBA_Object_ptr +CORBA_Object::_duplicate (CORBA_Object_ptr obj) +{ + if (obj) + obj->AddRef (); + return obj; +} + +// +// Null pointers represent nil objects. +// +CORBA_Object_ptr +CORBA_Object::_nil () +{ + return 0; +} + +CORBA_Boolean +CORBA_is_nil (CORBA_Object_ptr obj) +{ + return (CORBA_Boolean) (obj == 0); +} + + +// +// DII hook to objref +// +// The mapping for create_request is split into two forms, corresponding to +// the two usage styles described in CORBA section 6.2.1. +// +void +__stdcall +CORBA_Object::_create_request ( + const CORBA_Char *operation, + CORBA_NVList_ptr arg_list, + CORBA_NamedValue_ptr result, + CORBA_Request_ptr &request, + CORBA_Flags req_flags, + CORBA_Environment &env +) +{ + env.clear (); + request = new CORBA_Request (this, operation, arg_list, result, req_flags); +} + + +CORBA_Request_ptr +__stdcall +CORBA_Object::_request ( + const CORBA_Char *operation, + CORBA_Environment &env +) +{ + env.clear (); + return new CORBA_Request (this, operation); +} + + +// +// GET_INTERFACE ... send a simple call to the object, it returns +// an InterfaceDef objref. +// +static const paramdata Object_get_interface_params [] = { + { _tc_CORBA_Object, PARAM_RETURN, 0 } + // XXX should be tc_InterfaceDef +}; +static const calldata Object_get_interface_calldata = { + "_interface", CORBA_B_TRUE, + 1, &Object_get_interface_params [0], + 0, 0 +}; + + +CORBA_InterfaceDef_ptr +__stdcall +CORBA_Object::_get_interface ( + CORBA_Environment &env +) +{ + CORBA_InterfaceDef_ptr retval = 0; + + // + // At this time, we only have a single generic way to find the + // CORBA interface def for an object. + // + STUB_Object *istub; + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return retval; + } + Release (); + + // + // NOTE: If istub->type_id is nonzero, we could try asking a + // "local" interface repository and avoid costly network I/O. + // (It's wrong to have different data associated with the same + // interface ID in different repositories; the interface is + // the interface, it doesn't change!) + // + // We need to be prepared to ask the object itself for this + // information though, since there's no guarantee that any + // local interface repository will really have records of this + // particular interface. + // + istub->do_call (env, &Object_get_interface_calldata, &retval); + return retval; +} + + +// +// IS_A ... ask the object if it's an instance of the type whose logical +// type ID is passed as a parameter. +// +static const paramdata Object_is_a_params [] = { + { _tc_CORBA_Boolean, PARAM_RETURN, 0 }, + { _tc_CORBA_String, PARAM_IN, 0 } +}; +static const calldata Object_is_a_calldata = { + "_is_a", CORBA_B_TRUE, + 2, &Object_is_a_params [0], + 0, 0 +}; + + +CORBA_Boolean +__stdcall +CORBA_Object::_is_a ( + const CORBA_Char *type_id, + CORBA_Environment &env +) +{ + // + // At this time, we only have a single generic way to check the + // type of an object. + // + STUB_Object *istub; + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return CORBA_B_FALSE; + } + Release (); + + // + // NOTE: if istub->type_id is nonzero and we have local knowledge + // of it, we can answer this question without a costly remote call. + // + // That "local knowledge" could come from stubs or skeletons linked + // into this process in the best case, or a "near" repository in a + // slightly worse case. Or in a trivial case, if the ID being asked + // about is the ID we have recorded, we don't need to ask about the + // inheritance relationships at all! + // + // In real systems having local knowledge will be common, though as the + // systems built atop ORBs become richer it'll also become common to + // have the "real type ID" not be directly understood because it's + // more deeply derived than any locally known types. + // + // XXX if type_id is that of CORBA::Object, "yes, we comply" :-) + // + if (istub->type_id != 0 + && strcmp ((char *)type_id, (char *)istub->type_id) == 0) + return CORBA_B_TRUE; + + // + // Our local knowledge about this type is insufficient to say whether + // this reference is to an object of a type which "is_a" subtype of + // the type whose ID is passed as a parameter. The implementation + // always knows the answer to that question, however! + // + CORBA_Boolean retval = CORBA_B_FALSE; + + istub->do_call (env, &Object_is_a_calldata, &retval, &type_id); + return retval; +} + + +// +// GET_IMPLEMENTATION ... send a simple call to the object, it returns +// an ImplementationDef objref. +// +static const paramdata Object_get_implementation_params [] = { + { _tc_CORBA_Object, PARAM_RETURN, 0 } + // XXX should be tc_ImplementationDef +}; +static const calldata Object_get_implementation_calldata = { + "_implementation", CORBA_B_TRUE, + 1, &Object_get_implementation_params [0], + 0, 0 +}; + + +CORBA_ImplementationDef_ptr +__stdcall +CORBA_Object::_get_implementation ( + CORBA_Environment &env +) +{ + STUB_Object *istub; + CORBA_ImplementationDef_ptr retval = 0; + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return retval; + } + Release (); + + istub->do_call (env, &Object_get_implementation_calldata, &retval); + return retval; +} + + +// +// NON_EXISTENT ... send a simple call to the object, which will +// either elicit a FALSE response or a OBJECT_NOT_EXIST exception. +// In the latter case, return FALSE. +// +static const paramdata Object_non_existent_params [] = { + { _tc_CORBA_Boolean, PARAM_RETURN, 0 } +}; +static const calldata Object_non_existent_calldata = { + "_non_existent", CORBA_B_TRUE, + 1, &Object_non_existent_params [0], + 0, 0 +}; + + +CORBA_Boolean +__stdcall +CORBA_Object::_non_existent ( + CORBA_Environment &env +) +{ + CORBA_Boolean retval = CORBA_B_FALSE; + CORBA_Exception *x; + STUB_Object *istub; + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return CORBA_B_FALSE; + } + Release (); + + istub->do_call (env, &Object_non_existent_calldata, &retval); + + if ((x = env.exception ()) != 0) { + char *id; + + id = _tc_CORBA_OBJECT_NOT_EXIST->id (env); + if (env.exception () == 0 + && strcmp (id, x->id ()) == 0) { + env.clear (); + return CORBA_B_TRUE; + } + // + // reporting a "real" exception ... + // + return CORBA_B_FALSE; + } else { + env.clear (); + return CORBA_B_FALSE; + } +} + + +// +// Quickly hash an object reference's representation data. +// Used to create hash tables. +// +CORBA_ULong +__stdcall +CORBA_Object::_hash ( + CORBA_ULong maximum, + CORBA_Environment &env +) +{ + STUB_Object *istub; + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return CORBA_B_FALSE; + } + Release (); + + return istub->hash (maximum, env); +} + + +// +// Compare two object references to see if they point to the +// same object. Used in linear searches, as in hash buckets. +// +// XXX would be useful to also have a trivalued comparison predicate, +// such as strcmp(), to allow more comparison algorithms. +// +CORBA_Boolean +__stdcall +CORBA_Object::_is_equivalent ( + CORBA_Object_ptr other_obj, + CORBA_Environment &env +) +{ + STUB_Object *istub; + + if (other_obj == this) { + env.clear (); + return CORBA_B_TRUE; + } + + if (QueryInterface (IID_STUB_Object, (void **) &istub) != NOERROR) { + env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return CORBA_B_FALSE; + } + Release (); + + return istub->is_equivalent (other_obj, env); +} + + +// +// COM's IUnknown support +// + +#if unix +// +// XXX this is not the GUID that Microsoft uses. It can matter. +// + +// {77420089-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_IUnknown, +0x77420089, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); +#endif + +// {A201E4C2-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_Object, +0xa201e4c2, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_Object::AddRef () +{ + return parent->AddRef (); +} + +ULONG +__stdcall +CORBA_Object::Release () +{ + return parent->Release (); +} + +HRESULT +__stdcall +CORBA_Object::QueryInterface ( + REFIID riid, + void **ppv +) +{ + return parent->QueryInterface (riid, ppv); +} + diff --git a/TAO/IIOP/lib/runtime/orbobj.cpp b/TAO/IIOP/lib/runtime/orbobj.cpp new file mode 100644 index 00000000000..ae84d6bdd83 --- /dev/null +++ b/TAO/IIOP/lib/runtime/orbobj.cpp @@ -0,0 +1,317 @@ +// @(#)orbobj.cpp 1.8 95/09/24 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: CORBA::ORB operations +// +// XXX as with TOA, this has a strong coupling to the Internet ORB (IIOP) +// code. We should make it know less about that protocol component and +// have a loose table-driven coupling to ORB/protocol library components. +// + +#include <assert.h> +#include <limits.h> +#include <signal.h> +#include <string.h> + +#include <corba/orb.hh> +#include <corba/stub.hh> + +#include "runtime/debug.hh" +#include "runtime/thread.hh" + +#include "bridge/iioporb.hh" // XXX + +#include <initguid.h> + + +extern void __TC_init_table (); +extern void __TC_init_standard_exceptions (CORBA_Environment &env); + +#ifdef _POSIX_THREADS +static pthread_mutex_t refcnt_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + +#if defined (SIG_IGN_BROKEN) +# undef SIG_IGN +# define SIG_IGN ((RETSIGTYPE (*)(int))1) +#endif // NeXT + + +// +// Constructor and destructor are accessible to subclasses +// +CORBA_ORB::CORBA_ORB () +{ + _refcount = 1; +} + +CORBA_ORB::~CORBA_ORB () +{ + assert (_refcount == 0); +} + +// +// CORBA dup/release build on top of COM's (why not). +// +void +CORBA_release ( + CORBA_ORB_ptr obj +) +{ + if (obj) + obj->Release (); +} + +CORBA_ORB_ptr +CORBA_ORB::_duplicate (CORBA_ORB_ptr obj) +{ + if (obj) + obj->AddRef (); + return obj; +} + +// +// Null pointers represent nil objects. +// +CORBA_ORB_ptr +CORBA_ORB::_nil () +{ + return 0; +} + +CORBA_Boolean +CORBA_is_nil (CORBA_ORB_ptr obj) +{ + return (CORBA_Boolean) (obj == 0); +} + + +// +// COM's IUnknown support +// + +// {A201E4C6-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_ORB, +0xa201e4c6, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + +// {A201E4C7-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_STUB_Object, +0xa201e4c7, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_ORB::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + return _refcount++; +} + +ULONG +__stdcall +CORBA_ORB::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + assert (this != 0); + + if (--_refcount != 0) + return _refcount; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + delete this; + return 0; +} + + +// +// ORB initialisation, per OMG document 94-9-46. +// +// XXX in addition to the "built in" Internet ORB, there will be ORBs which +// are added separately, e.g. through a DLL listed in the registry. Registry +// will be used to assign orb names and to establish which is the default. +// +static CORBA_ORB_ptr the_orb; + +CORBA_ORB_ptr +CORBA_ORB_init ( + int &, // argc + char *const *, // argv + char *orb_name, + CORBA_Environment &env +) +{ +#ifdef _POSIX_THREADS + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + Critical region (&lock); +#endif // _POSIX_THREADS + + env.clear (); + + // + // Initialising is done only once. + // + // XXX Applications that can't tell if they've initialized (e.g. + // some library modules) must check for a particular error and ignore + // it if they get it. We need a minor code to indicate this case. + // In general, one-time initialization is suboptimal. + // + // XXX We also need to enable initialising more than one ORB!! + // + if (the_orb) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + + // + // Verify some of the basic implementation requirements. This test + // gets optimized away by a decent compiler (or else the rest of the + // routine does). + // + // NOTE: we still "just" assume that native floating point is IEEE. + // + if (sizeof (CORBA_Short) != 2 + || sizeof (CORBA_Long) != 4 + || sizeof (CORBA_LongLong) != 8 + || sizeof (CORBA_Float) != 4 + || sizeof (CORBA_Double) != 8 + || sizeof (CORBA_LongDouble) != 16 + || sizeof (CORBA_WChar) < 2 + || sizeof (void *) != SIZEOF_VOID_P + ) { + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + + // + // ignoring argc, argv for now -- no arguments we care about + // + // XXX should remove any of the "-ORB*" arguments that we know + // about ... and report errors for the rest. + // + +#ifdef DEBUG + // + // Make it a little easier to debug programs using this code. + // + { + char *value = getenv ("ORB_DEBUG"); + + if (value != 0) { + debug_level = atoi (value); + if (debug_level <= 0) + debug_level = 1; + dmsg1 ("debug_level == %d", debug_level); + } + } +#endif // DEBUG + + // + // On Win32, we should be collecting information from the Registry + // such as what ORBs are configured, specific configuration details + // like whether they generate IOR or URL style stringified objrefs + // and which addresses they listen to (e.g. allowing multihomed + // hosts to implement firewalls), user-meaningful orb names (they + // will normally indicate domains), and more. + // + // On UNIX, we should collect that from some private config file. + // + // Instead, this just treats the "internet" ORB name specially and + // makes it always use URL-style stringified objrefs, where the + // hostname and TCP port number are explicit (and the whole objref + // is readable by mortals). + // + CORBA_Boolean use_ior; + + if (orb_name != 0 && strcmp (orb_name, "internet") == 0) + use_ior = CORBA_B_FALSE; + else + use_ior = CORBA_B_TRUE; + +#ifdef SIGPIPE + // + // Impractical to have each call to the ORB protect against the + // implementation artifact of potential writes to dead connections, + // as it'd be way expensive. Do it here; who cares about SIGPIPE + // in these kinds of applications, anyway? + // + (void) signal (SIGPIPE, SIG_IGN); +#endif // SIGPIPE + +#if defined (_WINSOCKAPI_) + // + // winsock needs explicit initialization, and applications must manage + // versioning problems directly. + // + WSADATA wsadata; + + if (WSAStartup (MAKEWORD (1, 1), &wsadata) != 0) { + dsockerr ("WSAStartup"); + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } + if (LOBYTE (wsadata.wVersion) != 1 || HIBYTE (wsadata.wVersion != 1)) { + dmsg2 ("bad winsock version %d.%d", + HIBYTE (wsadata.wVersion), LOBYTE (wsadata.wVersion)); + env.exception (new CORBA_INITIALIZE (COMPLETED_NO)); + return 0; + } +#endif // _WINSOCKAPI_ + + // + // Call various internal initialization routines. + // + __TC_init_table (); + __TC_init_standard_exceptions (env); + if (env.exception () != 0) + return 0; + + // + // Inititalize the "ORB" pseudo-object now. + // + the_orb = new IIOP_ORB (use_ior); + + return the_orb; +} + +void +CORBA_ORB::create_list ( + CORBA_Long count, + CORBA_NVList_ptr &retval +) +{ + assert (count <= UINT_MAX); + + retval = new CORBA_NVList; + + if (count != 0) { + retval->_len = 0; + retval->_max = (unsigned) count; + retval->_values = (CORBA_NamedValue_ptr) calloc ((unsigned) count, + sizeof (CORBA_NamedValue)); + } +} + + +// +// This is a server-side internal routine; it's not available to any +// portable code except method code, which moreover may not access the +// state variable directly since its implemention may differ between ORBs. +// +// XXX it's server-side so should be OA-specific and not in this module +// +CORBA_ORB_ptr +_orb () +{ + return the_orb; +} diff --git a/TAO/IIOP/lib/runtime/principa.cpp b/TAO/IIOP/lib/runtime/principa.cpp new file mode 100644 index 00000000000..150031b5f68 --- /dev/null +++ b/TAO/IIOP/lib/runtime/principa.cpp @@ -0,0 +1,107 @@ +// @(#)principa.cpp 1.4 95/11/04 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: Principal identifier pseudo-objref +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include <initguid.h> + +#include "runtime/thread.hh" + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t principal_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + +void +CORBA_release (CORBA_Principal_ptr principal) +{ + if (principal) + principal->Release (); +} + +CORBA_Boolean +CORBA_is_nil (CORBA_Principal_ptr principal) +{ + return (CORBA_Boolean)(principal == 0); +} + +CORBA_Principal::CORBA_Principal () +{ +} + +CORBA_Principal::~CORBA_Principal () +{ + assert (_refcount == 0); + + if (id.buffer) + delete id.buffer; +} + +// +// For COM -- IUnKnown operations +// + +// {A201E4C0-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_Principal, +0xa201e4c0, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_Principal::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&principal_lock); +#endif + + return ++_refcount; +} + +ULONG +__stdcall +CORBA_Principal::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&principal_lock); +#endif + + if (--_refcount != 0) + return _refcount; + +#ifdef _POSIX_THREADS + region.leave (); +#endif + + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_Principal::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_Principal == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} diff --git a/TAO/IIOP/lib/runtime/request.cpp b/TAO/IIOP/lib/runtime/request.cpp new file mode 100644 index 00000000000..2143603ae2c --- /dev/null +++ b/TAO/IIOP/lib/runtime/request.cpp @@ -0,0 +1,168 @@ +// @(#)request.cpp 1.6 95/09/24 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Implementation of Dynamic Invocation Interface +// +#include <corba/orb.hh> +#include <corba/stub.hh> + +#include <initguid.h> + +#include "runtime/debug.hh" +#include "runtime/cdr.hh" +#include "runtime/thread.hh" + + +// {77420085-F276-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_Request, +0x77420085, 0xf276, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + +#ifdef _POSIX_THREADS +static pthread_mutex_t refcnt_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + +ULONG +__stdcall +CORBA_Request::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + return _refcount++; +} + +ULONG +__stdcall +CORBA_Request::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + assert (this != 0); + + if (--_refcount != 0) + return _refcount; + + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_Request::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_Request == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + +// +// Reference counting for DII Request object +// +void +CORBA_release (CORBA_Request_ptr req) +{ + if (req) + req->Release (); +} + +CORBA_Boolean +CORBA_is_nil (CORBA_Request_ptr req) +{ + return (CORBA_Boolean)(req == 0); +} + +// +// DII Request class implementation +// +CORBA_Request::CORBA_Request ( + CORBA_Object_ptr obj, + const CORBA_Char *op, + CORBA_NVList_ptr args, + CORBA_NamedValue_ptr result, + CORBA_Flags flags +) : + _args (args), + _result (result), + _flags (flags), + _refcount (1) +{ + _target = CORBA_Object::_duplicate (obj); + _opname = CORBA_string_copy (op); +} + + +CORBA_Request::CORBA_Request ( + CORBA_Object_ptr obj, + const CORBA_Char *op +) : + _flags (0), + _refcount (1) +{ + _target = CORBA_Object::_duplicate (obj); + _opname = CORBA_string_copy (op); + + _args = new CORBA_NVList; + _result = new CORBA_NamedValue; +} + +CORBA_Request::~CORBA_Request () +{ + assert (_refcount == 0); + + CORBA_release (_target); + CORBA_string_free ((CORBA_String)_opname); + CORBA_release (_args); + CORBA_release (_result); +} + +// +// The public DII interfaces: normal and oneway calls. +// +// NOTE that using DII, programmers can get the special behaviour of +// discarding the response for normal calls. This doesn't change the +// semantics of any OMG-IDL interface, it just streamlines control flow +// in some exotic situations. +// +void +CORBA_Request::invoke () +{ + STUB_Object *stub; + + if (_target->QueryInterface (IID_STUB_Object, (void **) &stub) != NOERROR) { + _env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return; + } + + stub->do_dynamic_call ((char *)_opname, CORBA_B_TRUE, + _args, _result, _flags, _exceptions, _env); + stub->Release (); +} + +void +CORBA_Request::send_oneway () +{ + STUB_Object *stub; + + if (_target->QueryInterface (IID_STUB_Object, (void **) &stub) != NOERROR) { + _env.exception (new CORBA_DATA_CONVERSION (COMPLETED_NO)); + return; + } + stub->do_dynamic_call ((char *)_opname, CORBA_B_TRUE, + _args, _result, _flags, _exceptions, _env); + stub->Release (); +} diff --git a/TAO/IIOP/lib/runtime/t-xdr.cpp b/TAO/IIOP/lib/runtime/t-xdr.cpp new file mode 100644 index 00000000000..fcfb99fd72c --- /dev/null +++ b/TAO/IIOP/lib/runtime/t-xdr.cpp @@ -0,0 +1,268 @@ +// XDR record stream ... test encode performance against CDR, using +// normal kinds of inlining performance hacks +// +// XXX as of 3-Nov-95 XDR_stream should only be relied on to marshal the +// simplest primitives ... not objrefs, typecodes, etc. Also, the +// handling of sequences of chars/octets/shorts/wchars is wrong. +// +// It's suitable only for a simple performance test just now ... +// + + +#include <string.h> +#include <unistd.h> +#include <widec.h> + +#include <sys/types.h> +#include <sys/time.h> + +#include <corba/orb.hh> + +#include "runtime/cdr.hh" +#include "runtime/debug.hh" + +#include "onc/xdr.hh" + + +// A structure that's somewhat representative of an IIOP message +// in terms of overall complexity, so that its encoding cost is +// "interesting" + +typedef CORBA_SEQUENCE <CORBA_Octet> opaque; + +struct interesting { + +// A call that's mostly going to be "on the mark" for IIOP-ish +// messages with small numbers of parameters: + +// 4 chars magic +// 2 bytes version +// 1 byte byte-order +// 1 byte message type ---> 8 bytes "pure header" + CORBA_Char hdr_bytes [8]; +// 1 word message size --=> end of GIOP::MessageHeader + CORBA_ULong hdr_len; + +// Service Context (1 word min) + CORBA_ULong empty_ctx; +// 1 word request id + CORBA_ULong request_id; +// 1 byte response-expected flag + CORBA_Boolean response_expected; +// [ CDR: 3 bytes padding ] +// opaque object key (1 word min; typically less than 16 bytes) + opaque object_key; +// string operation name (non-empty ... often less than 10 bytes) + CORBA_String opname; +// Principal client (1 word min) + opaque client_id; + +// --=> END OF GIOP::RequestHeader + +// ... small number of parameters with any significance + +// 2 word parameters + CORBA_ULong param1; + CORBA_ULong param2; +// 1 string parameter + CORBA_String param3; + +}; + +// XXX declare CDR typecode for above type ... initialize and use +// one instance in the test below + +extern CORBA_TypeCode TC_opaque; + +static void +do_test ( + int use_XDR, + CORBA_TypeCode_ptr tc, + void *data +) +{ + unsigned loopcount = 100 * 1000; + unsigned i; + unsigned error_count = 0; + timeval before, after; + CORBA_String opname = "kill_husband"; + opaque key; + CORBA_Boolean status; + + key.buffer = (CORBA_Octet *) "jacqueline"; + key.length = key.maximum = 10; + + if (gettimeofday (&before, 0) < 0) + dperror ("gettimeofday before"); + + if (use_XDR) { + + // Using XDR APIs and encoding rules ... + // encode the structure repeatedly + + for (i = 0; i < loopcount; i++) { + CORBA_Environment env; + XDR_stream stream (-1); + + // GIOP header plus most of request header + status = status + && stream.put_long ('GIOP') // magic + && stream.put_long ('\01\01\01\01') // version etc + && stream.put_long (99) // msg len + && stream.put_long (0) // no svc ctx + && stream.put_long (42) // request ID + && stream.put_boolean (CORBA_B_TRUE)// response? + ; + + if (status) + status = XDR_stream::encoder (&TC_opaque, &key, 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + if (status) + status = XDR_stream::encoder (_tc_CORBA_String, &opname, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + /* + if (status) + status = XDR_stream::encoder (_tc_CORBA_Principal, &key, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + */ + + // Parameters: two longs, a string + status = status + && stream.put_long (99) + && stream.put_long (-3455); + if (status) + status = XDR_stream::encoder (_tc_CORBA_String, &opname, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + // Gratuitous extra "interesting" data + status = XDR_stream::encoder (tc, data, 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + + if (status != CORBA_B_TRUE) + error_count++; + } + + } else { + + // This branch is the same, but using CDR APIs and encoding ... + // encode the structure repeatedly + + for (i = 0; i < loopcount; i++) { + CORBA_Environment env; + unsigned char buffer [CDR::DEFAULT_BUFSIZE]; + CDR stream (buffer, sizeof buffer); + + // GIOP header plus most of request header + status = status + && stream.put_long ('GIOP') // magic + && stream.put_long ('\01\01\01\01') // version etc + && stream.put_long (99) // msg len + && stream.put_long (0) // no svc ctx + && stream.put_long (42) // request ID + && stream.put_boolean (CORBA_B_TRUE)// response? + ; + + if (status) + status = CDR::encoder (&TC_opaque, &key, 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + if (status) + status = CDR::encoder (_tc_CORBA_String, &opname, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + /* + if (status) + status = CDR::encoder (_tc_CORBA_Principal, &key, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + */ + + // Parameters: two longs, a string + status = status + && stream.put_long (99) + && stream.put_long (-3455); + if (status) + status = CDR::encoder (_tc_CORBA_String, &opname, + 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + // Gratuitous extra "interesting" data + status = CDR::encoder (tc, data, 0, &stream, env) + == CORBA_TypeCode::TRAVERSE_CONTINUE; + + + if (status != CORBA_B_TRUE) + error_count++; + } + + } + + if (gettimeofday (&after, 0) < 0) + dperror ("gettimeofday after"); + + if (loopcount > 0) { + if (error_count == 0) { + unsigned long us; + + us = after.tv_sec - before.tv_sec; + us *= 1000 * 1000; + us += after.tv_usec - before.tv_usec; + us /= loopcount; + + printf ("%s average encode time\t= %ld.%.03ldms, \t" + "%ld calls/second\n", + use_XDR ? "XDR" : "CDR", + us / 1000, us % 1000, + 1000000L / us); + } + + printf ("%d calls, %d errors\n", loopcount, error_count); + } +} + + +int +main ( + int argc, + char **argv +) +{ + int c; + int use_XDR = 1; + CORBA_TypeCode_ptr tc = _tc_CORBA_TypeCode; + void *data = tc; + + while ((c = getopt (argc, argv, "cx")) != EOF) { + switch (c) { + case 'c': + use_XDR = 0; + continue; + + case 'x': + use_XDR = 1; + continue; + + case '?': + default: +// usage: + fprintf (stderr, "usage: %s" + , " [-cx]" + , "\n" + , argv [0] + ); + } + } + + do_test (1, tc, data); // XDR-ish + do_test (0, tc, data); // CDR + + return 0; +} + diff --git a/TAO/IIOP/lib/runtime/tc_const.cpp b/TAO/IIOP/lib/runtime/tc_const.cpp new file mode 100644 index 00000000000..2ec2be262e8 --- /dev/null +++ b/TAO/IIOP/lib/runtime/tc_const.cpp @@ -0,0 +1,133 @@ +// @(#)tc_const.cpp 1.3 95/09/12 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// ORB: All the CORBA-specified typecode constants. +// +// NOTE: IFR TypeCode constants aren't here; they're left for an IDL +// compiler to generate from the appropriate IDL source. +// +// NOTE: it'd be nice to have these not use init sections. Most can easily +// be in readonly data (e.g. text segment, ROM) rather than writable data; +// that speeds program startup and page sharing in shared libraries. +// +// THREADING NOTE: no issues, these are immutable constants +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +// +// Null and void +// +static CORBA_TypeCode tc_null (tk_null); +const CORBA_TypeCode_ptr _tc_CORBA_Null = (CORBA_TypeCode_ptr) &tc_null; + +CORBA_TypeCode_ptr +CORBA_TypeCode::_nil () +{ + return &tc_null; +} + +static CORBA_TypeCode tc_void (tk_void); +const CORBA_TypeCode_ptr _tc_CORBA_Void = &tc_void; + +// +// Basic numeric types: short, long, longlong, and unsigned variants +// +static CORBA_TypeCode tc_short (tk_short); +const CORBA_TypeCode_ptr _tc_CORBA_Short = &tc_short; + +static CORBA_TypeCode tc_long (tk_long); +const CORBA_TypeCode_ptr _tc_CORBA_Long = &tc_long; + +static CORBA_TypeCode tc_longlong (tk_longlong); +const CORBA_TypeCode_ptr _tc_CORBA_LongLong = &tc_longlong; + +static CORBA_TypeCode tc_ushort (tk_ushort); +const CORBA_TypeCode_ptr _tc_CORBA_UShort = &tc_ushort; + +static CORBA_TypeCode tc_ulong (tk_ulong); +const CORBA_TypeCode_ptr _tc_CORBA_ULong = &tc_ulong; + +static CORBA_TypeCode tc_ulonglong (tk_ulonglong); +const CORBA_TypeCode_ptr _tc_CORBA_ULongLong = &tc_ulonglong; + +// +// Floating point types: single, double, quad precision +// +static CORBA_TypeCode tc_float (tk_float); +const CORBA_TypeCode_ptr _tc_CORBA_Float = &tc_float; + +static CORBA_TypeCode tc_double (tk_double); +const CORBA_TypeCode_ptr _tc_CORBA_Double = &tc_double; + +static CORBA_TypeCode tc_longdouble (tk_longdouble); +const CORBA_TypeCode_ptr _tc_CORBA_LongDouble = &tc_longdouble; + +// +// Various simple quantities +// +static CORBA_TypeCode tc_boolean (tk_boolean); +const CORBA_TypeCode_ptr _tc_CORBA_Boolean = &tc_boolean; + +static CORBA_TypeCode tc_octet (tk_octet); +const CORBA_TypeCode_ptr _tc_CORBA_Octet = &tc_octet; + +// +// Internationalization-related data types: ISO Latin/1 and "wide" +// characters, and strings of each. "wchar" is probably Unicode 1.1, +// "wstring" being null-terminated sets thereof. +// +static CORBA_TypeCode tc_char (tk_char); +const CORBA_TypeCode_ptr _tc_CORBA_Char = &tc_char; + +static CORBA_TypeCode tc_wchar (tk_wchar); +const CORBA_TypeCode_ptr _tc_CORBA_WChar = &tc_wchar; + +static CORBA_TypeCode tc_string (tk_string); +const CORBA_TypeCode_ptr _tc_CORBA_String = &tc_string; + +static CORBA_TypeCode tc_wstring (tk_wstring); +const CORBA_TypeCode_ptr _tc_CORBA_WString = &tc_wstring; + +// +// Various things that can be passed as "general" parameters: +// Any, TypeCode_ptr, Principal_ptr, Object_ptr +// +static CORBA_TypeCode tc_any (tk_any); +const CORBA_TypeCode_ptr _tc_CORBA_Any = &tc_any; + +static CORBA_TypeCode tc_typecode (tk_TypeCode); +const CORBA_TypeCode_ptr _tc_CORBA_TypeCode = &tc_typecode; + +static CORBA_TypeCode tc_principal (tk_Principal); +const CORBA_TypeCode_ptr _tc_CORBA_Principal = &tc_principal; + +// +// typecode for objref is complex, has two string parameters +// +// NOTE: Must be four-byte aligned +// +static const unsigned char oc_objref [] = { + 0, 0, 0, 0, // big endian encoding (+ padding) + 0, 0, 0, 29, // 29 char string + 3 pad bytes + 'I', 'D', 'L', ':', + 'o', 'm', 'g', '.', + 'o', 'r', 'g', '/', + 'C', 'O', 'R', 'B', + 'A', '/', 'O', 'b', + 'j', 'e', 'c', 't', + ':', '1', '.', '0', + '\0', 0, 0, 0, + 0, 0, 0, 7, // 7 chars "Object" + 1 pad byte + 'O', 'b', 'j', 'e', + 'c', 't', '\0', 0, +}; +static CORBA_TypeCode + tc_objref (tk_objref, sizeof oc_objref, + (unsigned char *)&oc_objref, CORBA_B_FALSE); +const CORBA_TypeCode_ptr _tc_CORBA_Object = &tc_objref; + diff --git a/TAO/IIOP/lib/runtime/thread.hh b/TAO/IIOP/lib/runtime/thread.hh new file mode 100644 index 00000000000..372ca7bcfe8 --- /dev/null +++ b/TAO/IIOP/lib/runtime/thread.hh @@ -0,0 +1,185 @@ +// @(#)thread.hh 1.3 95/09/29 +// Copyright 1995 by Sun Microsystems, Inc +// All Rights Reserved +// +// THREADING: simple thread utility classes +// +// Instances of these classes are placed at the beginning of a lexical +// scope to ensure that all exits from that scope restore thread or +// synchronization state to its original values. Thread state includes +// ansynchronous cancellability, synchronization state includes locks +// that cover data structures that are shared between threads. +// +// NOTE: supports only POSIX and SVR4 threads for the moment. Win32 +// threads can be supported sometime later. +// + +#ifndef _THREAD_HH +#define _THREAD_HH + +#ifdef unix +#include <unistd.h> // may define _POSIX_THREADS +#endif // unix + +#ifdef _POSIX_THREADS +#include <pthread.h> + +// +// This ugliness is called for by OSF/1 v3.0, which defines the POSIX.1c +// symbol "_POSIX_THREADS" even though it is noncompliant. +// +// We make the assumption that PTHREAD_MUTEX_INITIALIXER is a preprocessor +// symbol, although POSIX does not require that, since since it works on +// all the POSIX.1c implementations we've seen. +// +# ifndef PTHREAD_MUTEX_INITIALIZER +# undef _POSIX_THREADS +# endif // not really POSIX.1c threads +#endif + +// +// Use native threading on Solaris 2.2 and later. +// +// XXX this is a temporary kluge; "autoconf" should kick in the +// appropriate POSIX threads emulation when it's not native. +// +#if defined (sun) && !defined (_POSIX_THREADS) && !defined (FAKE_POSIX_THREADS) +# define FAKE_POSIX_THREADS +#endif // 2.2 <= "solaris version < 2.5 + + +#ifdef FAKE_POSIX_THREADS +// +// To use SVR4 native threads, enable FAKE_POSIX_THREADS. This is +// intentionally not autoconfigured, since it's only really needed on +// Solaris versions before 2.5. POSIX threads are the way to go +// longer term, on all compliant platforms. +// +// NOTE: this is only a partial implementation !!! Enough to make +// the IIOP engine work as it's coded as of 9-Aug-95. If you start +// to use different POSIX options, these macros could stop working. +// +#define _POSIX_THREADS // only as much as is defined here! + +#include <thread.h> +#include <synch.h> + +typedef thread_t pthread_t; +typedef mutex_t pthread_mutex_t; +typedef cond_t pthread_cond_t; +typedef unsigned long pthread_attr_t; +typedef thread_key_t pthread_key_t; + + +#define PTHREAD_MUTEX_INITIALIZER DEFAULTMUTEX +#define PTHREAD_COND_INITIALIZER DEFAULTCV + +#define pthread_mutex_lock mutex_lock +#define pthread_mutex_unlock mutex_unlock +#define pthread_cond_wait cond_wait + +struct pthread_once_t { + mutex_t lock; + int called_once; +}; + +#define PTHREAD_ONCE_INIT { DEFAULTMUTEX, 0 } + +inline void +pthread_once (pthread_once_t *var, void (*fn)()) +{ + mutex_lock (&var->lock); + if (var->called_once == 0) { + (fn)(); + var->called_once = 1; + } + mutex_unlock (&var->lock); +} + +#define pthread_self thr_self + +#define pthread_key_create thr_keycreate +#define pthread_attr_init(ap) ((*ap) = 0) +#define pthread_attr_setdetachstate(ap,v) ((*ap) |= (v)) + +#define PTHREAD_CREATE_DETACHED THR_DETACHED + +inline void * +pthread_getspecific (pthread_key_t key) +{ + void *vp; + + thr_getspecific (key, &vp); + return vp; +} + +#define pthread_setspecific thr_setspecific + +inline int +pthread_create ( + pthread_t *tidp, + pthread_attr_t *attrs, + void *(func)(void *), + void *arg +) +{ + return thr_create (0, 0, func, arg, *attrs, tidp); +} + +#endif // FAKE_POSIX_THREADS + + +#ifdef _POSIX_THREADS +// +// Stick one of these at the beginning of a block that hosts a critical +// section, passing it a pointer to the lock guarding that section. The +// destructor will release it on all code paths, eliminating one class of +// common threading errors. +// +class Critical { + public: + Critical (pthread_mutex_t *l) : lock (l) + { enter (); } + + ~Critical () + { leave (); } + + void leave () + { (void) pthread_mutex_unlock (lock); } + void enter () + { (void) pthread_mutex_lock (lock); } + void pause (pthread_cond_t *condition) + { (void) pthread_cond_wait (condition, lock); } + + private: + pthread_mutex_t *lock; +}; + + +// +// Stick one of these at the beginning of a block that can't support +// asynchronous cancellation, and which must be cancel-safe. +// +class ForceSynchronousCancel { + public: + // + // MIT Pthreads 1.60 doesn't include cancellation; this is good, it + // gives time to ensure that stack unwinding for cancellation (in C) + // interoperates with unwinding for C++ exceptions so that both + // resource reclamation systems interwork correctly. + // +#ifdef PTHREAD_CANCEL_DEFERRED + ForceSynchronousCancel () + { (void) pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, &old_type); } + + ~ForceSynchronousCancel () + { (void) pthread_setcanceltype (old_type, &old_type); } + + private: + int old_type; +#endif // PTHREAD_CANCEL_DEFERRED +}; + +#endif // _POSIX_THREADS + +#endif // _THREAD_HH diff --git a/TAO/IIOP/lib/runtime/toa.cpp b/TAO/IIOP/lib/runtime/toa.cpp new file mode 100644 index 00000000000..e60191a80e7 --- /dev/null +++ b/TAO/IIOP/lib/runtime/toa.cpp @@ -0,0 +1,146 @@ +// @(#)toa.cpp 1.3 95/09/29 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TOA initialisation -- both anonymous and (for system bootstrapping) +// named TOAs. +// +// XXX at this time, there's a strong linkage between this code and +// the modules knowing about IIOP. In the future, a looser coupling +// between OA initialiszation and protocol components is desired. +// + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#if unix +#include <unistd.h> +#include <netdb.h> + +#else +#include <winsock.h> + +#endif + +#include <corba/orb.hh> +#include <corba/toa.hh> + +#include "runtime/thread.hh" +#include "runtime/debug.hh" + +// XXX this should not know implementation or other details of any +// protocol modules! This is an implementation shortcut only. + +#include "bridge/iioporb.hh" +#include "bridge/connmgr.hh" +#include "bridge/tcpoa.hh" + +#include <initguid.h> + + +// {A201E4C8-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_TOA, +0xa201e4c8, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +// +// A "Named TOA" is used in bootstrapping some part of the ORB since +// it's name-to-address binding is managed by the OS. Examples of such +// bindings are /etc/services (for TCP) and /etc/rpc (for ONC RPC). The +// nam" of a TOA is only guaranteed to be unique within the domain of a +// single system, as a rule; two hosts would have distinct "king" TOAs. +// +// For network endpoints, most such names are manually administered. +// Some other namespaces (AF_UNIX filesystem names for example) have a +// more formal underlying name service that can be dynamically updated +// while not compromising sysem security. +// +// The address family used by the TOA is found from the ORB passed in. +// +// XXX the coupling could stand to be looser here, so this module did +// not know specifically about the Internet ORB !! +// +TOA_ptr +TOA::get_named_toa ( + CORBA_ORB_ptr orb, + CORBA_String name, + CORBA_Environment &env +) +{ + env.clear (); + + // + // If the ORB is an Internet ORB, we know this must be a TCP OA. + // + { + IIOP_ORB *internet; + + if (orb->QueryInterface (IID_IIOP_ORB, (void **)&internet) + == NOERROR) { + TCP_OA *tcp_oa; + + internet->Release (); + + // + // TCP_OA initialization with name specified; it'll + // come from /etc/services if it's not a port number. + // + tcp_oa = TCP_OA::init (orb, name, env); + if (env.exception () != 0) + return 0; + else + return tcp_oa; // derives from TOA + } + } + + // + // We don't know how to deal with this kind of ORB. Report error. + // + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; +} + + +// +// An "Anonymous" TOA is used more routinely. The name used doesn't +// matter to anyone; it is only used to create object references with +// a short lifespan, namely that of the process acquiring this TOA. +// +TOA_ptr +TOA::get_toa ( + CORBA_ORB_ptr orb, + CORBA_Environment &env +) +{ + env.clear (); + + // + // If the ORB is an Internet ORB, we know this must be a TCP OA. + // + { + IIOP_ORB *internet; + + if (orb->QueryInterface (IID_IIOP_ORB, (void **)&internet) + == NOERROR) { + TCP_OA *tcp_oa; + + internet->Release (); + + // + // TCP_OA initialization with null name means anonymous OA + // + tcp_oa = TCP_OA::init (orb, 0, env); + if (env.exception () != 0) + return 0; + else + return tcp_oa; // derives from TOA + } + } + + // + // We don't know how to deal with this kind of ORB. Report error. + // + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; +} diff --git a/TAO/IIOP/lib/runtime/typecode.cpp b/TAO/IIOP/lib/runtime/typecode.cpp new file mode 100644 index 00000000000..c18806cf639 --- /dev/null +++ b/TAO/IIOP/lib/runtime/typecode.cpp @@ -0,0 +1,712 @@ +// @(#)typecode.cpp 1.4 95/09/19 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TYPECODE: basic implementation of TypeCodes +// +// Typecodes essentially consist of just the CDR octets that get marshaled +// and unmarshaled, and this code knows how to parse those octets and answer +// questions CORBA's TypeCode APIs require. +// +// NOTE: This isn't well tuned performance-wise. Given how much is variable +// (byte order, alignment) it's clear tuning has its limits with respect to +// CDR bytecode interpretation. +// +// THREADING NOTE: Typecodes are readonly data structures, and the only +// mutual exclusion relates to reference counting and construction. +// + +#include <assert.h> +#include <limits.h> +#include <string.h> +#include <corba/orb.hh> + +#include "runtime/debug.hh" +#include "runtime/cdr.hh" +#include "runtime/thread.hh" + +#include <memory.h> +#include <sys/types.h> + +#include <initguid.h> + + +#ifdef _POSIX_THREADS +// +// If POSIX threads are available, set up lock covering refcounts. +// +static pthread_mutex_t refcnt_lock = PTHREAD_MUTEX_INITIALIZER; +#endif // _POSIX_THREADS + + +void +CORBA_release (CORBA_TypeCode_ptr tc) +{ + if (tc) + tc->Release (); +} + +CORBA_Boolean +CORBA_is_nil (CORBA_TypeCode_ptr tc) +{ + return (CORBA_Boolean) (tc == 0); +} + +// +// Constructor for CONSTANT typecodes with empty parameter lists. +// These are only created once, and those constants are shared. +// +CORBA_TypeCode::CORBA_TypeCode ( + CORBA_TCKind kind +) : + _length (0), + _buffer (0), + _kind (kind), + _parent (0), + _refcount (1), + _orb_owns (CORBA_B_FALSE) +{ +} + + +// +// Constructor for all other typecodes, including constants with non-empty +// parameter lists. See "corba.hh" for details. +// +CORBA_TypeCode::CORBA_TypeCode ( + CORBA_TCKind kind, + CORBA_ULong length, + CORBA_Octet *buffer, + CORBA_Boolean orb_owns_tc +) : + _length (length), + _buffer (buffer), + _kind (kind), + _parent (0), + _refcount (1), + _orb_owns (orb_owns_tc) +{ + // + // The CDR code used to interpret TypeCodes requires in-memory + // alignments to match the "on-the-wire" alignments, simplifying + // algorithms used to marshal/unmarshal. + // + // However, it's often hard to get compilers (in particular) to + // generate data that's so aligned, since C++ doesn't provide + // primitives giving control at that low a level. Although there + // are ways to get that alignment which work in almost all cases, + // we need to ensure adequate alignment in _all_ cases. + // + // This code exists to ensure such alignment; since the constructor + // is intended only for use by an IDL compiler or ORB code, it's + // not currently a priority to ensure the allocated code is freed. + // + if ((((ptr_arith_t)buffer) & 0x03) != 0) { + ptr_arith_t temp; + + temp = (ptr_arith_t) malloc (length + 4); + temp += 3; + temp &= ~0x03; + _buffer = (CORBA_Octet *) temp; + + (void) memcpy (_buffer, buffer, (size_t) length); + _orb_owns = CORBA_B_FALSE; // XXX may leak + } +} + + +// +// Destructor. For "indirected" typecodes, the typecode reuses the buffer +// owned by its parent, and so rather than deleting the buffer it just drops +// the parent's refcount. +// +CORBA_TypeCode::~CORBA_TypeCode () +{ + if (_parent) + _parent->Release (); + else if (_orb_owns) + delete _buffer; +} + + +// +// COM's IUnknown support +// +// + +// {A201E4C1-F258-11ce-9598-0000C07CA898} +DEFINE_GUID (IID_CORBA_TypeCode, +0xa201e4c1, 0xf258, 0x11ce, 0x95, 0x98, 0x0, 0x0, 0xc0, 0x7c, 0xa8, 0x98); + + +ULONG +__stdcall +CORBA_TypeCode::AddRef () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + return _refcount++; +} + +ULONG +__stdcall +CORBA_TypeCode::Release () +{ +#ifdef _POSIX_THREADS + Critical region (&refcnt_lock); +#endif + + assert (this != 0); + + if (--_refcount != 0) + return _refcount; + if (_orb_owns) + delete this; + return 0; +} + +HRESULT +__stdcall +CORBA_TypeCode::QueryInterface ( + REFIID riid, + void **ppv +) +{ + *ppv = 0; + + if (IID_CORBA_TypeCode == riid || IID_IUnknown == riid) + *ppv = this; + + if (*ppv == 0) + return ResultFromScode (E_NOINTERFACE); + + (void) AddRef (); + return NOERROR; +} + + +// +// just fetch the 'kind' field out of the typecode +// +CORBA_TCKind +CORBA_TypeCode::kind ( + CORBA_Environment &env +) const +{ + env.clear (); + return _kind; +} + + +// +// skip a typecode encoding in a given CDR stream +// +static CORBA_Boolean +skip_typecode( + CDR &stream +) +{ + CORBA_ULong kind; + CORBA_ULong temp; + + if (!stream.get_ulong (kind) + || kind >= TC_KIND_COUNT) + return CORBA_B_FALSE; + + switch (kind) { + // + // Most TypeCodes have empty parameter lists, nothing to skip + // + default: + break; + + // + // Some have single integer parameters, easy to skip. + // + // have preallocated constants that could be used. + // + case tk_string: + case tk_wstring: + case ~0: + return stream.get_ulong (temp); + + // + // The rest have "complex" parameter lists that are + // encoded as bulk octets ... just skip them + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_sequence: + case tk_array: + case tk_alias: + case tk_except: + return stream.get_ulong (temp) != CORBA_B_FALSE + && stream.skip_bytes (temp) != CORBA_B_FALSE; + } + + return CORBA_B_TRUE; +} + + +// +// Return member labels for tk_union typecodes. +// +CORBA_Any_ptr +CORBA_TypeCode::member_label ( + CORBA_ULong n, + CORBA_Environment &env +) const +{ + env.clear (); + + CDR stream; + + stream.setup_encapsulation(_buffer, (size_t)_length); + + // + // this function is only applicable to the tk_union TC + // + if (_kind != tk_union) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // skip ID and name, and then get the discriminant TC + // + CORBA_TypeCode_ptr tc = 0; + + if (!stream.skip_string () // type ID, hidden + || !stream.skip_string () // typedef name + || CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // skip default used, and get member count + // + CORBA_ULong member_count; + + if (!stream.get_ulong (member_count) // default used + || !stream.get_ulong (member_count) + ) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + // + // If caller asked for the label for a nonexistent member, they + // get an error report! + // + if (n >= member_count) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; + } + + // + // Get the n-th member label; they're all the same size and have + // no nested pointers, so we just overwrite each one with the + // enxt parameter. + // + void *buf = new CORBA_Octet [tc->size (env)]; + + if (env.exception () != 0) + return 0; + + for (CORBA_ULong i = 0; i <= n; i++) { + if (CDR::decoder (tc, buf, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE + || !stream.skip_string () // member name + || !skip_typecode (stream)) { // member TC + delete buf; + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + + // + // return the member label as an any + // + CORBA_Any *retval; + + retval = new CORBA_Any(tc, buf, CORBA_B_TRUE); + tc->Release (); + return retval; +} + + +// +// say how many parameters this typecode has; normally a fixed number, some +// are variable length. +// +// NOTE: This refers to "real" parameters, not what shows up in the IFR +// spec !! That is, "hidden" parameters are counted here, this doesn't +// strictly comply with what CORBA says "param_count" provides. +// +CORBA_ULong +CORBA_TypeCode::param_count ( + CORBA_Environment &env +) const +{ + env.clear (); + + switch (_kind) { + default: + return 0; + + case tk_string: + case tk_wstring: + return 1; + + case tk_objref: + case tk_sequence: + case tk_array: + return 2; + + case tk_alias: + return 3; + + case tk_except: + case tk_struct: + { + CORBA_ULong members; + CDR stream; + + stream.setup_encapsulation(_buffer, (size_t)_length); + + // skip rest of header (type ID and name) and collect the + // number of struct members + if (!stream.skip_string () // ID + || !stream.skip_string () // struct name + || !stream.get_ulong (members)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + return 3 + 2 * members; + } + case tk_enum: + { + CORBA_ULong members; + CDR stream; + + stream.setup_encapsulation(_buffer, (size_t)_length); + + // skip rest of header (type ID and name) and collect the + // number of struct members + if (!stream.skip_string () // ID + || !stream.skip_string () // typedef name + || !stream.get_ulong (members)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + return 3 + members; + } + case tk_union: + { + CORBA_ULong members; + CDR stream; + + stream.setup_encapsulation(_buffer, (size_t)_length); + + // skip rest of header (type ID, name, etc...) and collect the + // number of struct members + if (!stream.skip_string () // ID + || !stream.skip_string () // struct name + || !skip_typecode (stream) // discriminant TC + || !stream.get_ulong (members) // default used + || !stream.get_ulong (members) // real member count + ) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + return 5 + 3 * members; + } + } +} + + +// +// Internal hack, used until member_count() and length() are implemented. +// Doesn't support all the types that those routines support. +// +CORBA_ULong +CORBA_TypeCode::ulong_param ( + CORBA_ULong n, + CORBA_Environment &env +) const +{ + CORBA_ULong temp; + + temp = param_count (env); // clears env + if (env.exception ()) + return 0; + + if (temp < n) { + env.exception (new CORBA_Bounds); + return 0; + } + + // + // Get parameters for non-empty typecodes; their parameter lists are + // encapsulated CDR (for complex ones) or inlined (for simple ones). + // + switch (_kind) { + default: // most have no long params + break; + + // + // Array, sequence ... complex parameter lists + // + case tk_array: // param 1 is an integer + case tk_sequence: // ... identical content + { + if (n == 0) + break; + + // + // Build CDR stream for encapsulated params, and skip the + // typecode up front. + // + CDR stream; + + stream.setup_encapsulation (_buffer, (size_t)_length); + if (!skip_typecode (stream)) { + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; + } + + // + // Then comes the "bounds" parameter. + // + if (!stream.get_ulong (temp)) + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return temp; + } + + // + // string, wstring ... simple parameter lists, containing just + // the string bounds (zero indicates unbounded). Stored specially + // + case tk_string: + case tk_wstring: + if (n != 0) + break; + return _length; + } + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; +} + + +// +// Internal hack, used until member_type(), discriminator_type(), +// and content_type() are implemented. +// +// NOTE special calling convention for CDR::decoder() when we're +// potentially deencapsulating an indirected typecode: the "data2" +// value indicates that this typecode is the parent. See comments +// at CDR::decoder() for further details. +// +CORBA_TypeCode_ptr +CORBA_TypeCode::typecode_param ( + CORBA_ULong n, + CORBA_Environment &env +) const +{ + CORBA_ULong temp; + + temp = param_count (env); // clears env + if (env.exception ()) + return 0; + + if (temp < n) { + env.exception (new CORBA_Bounds); + return 0; + } + + // + // Build the de-encapsulating CDR stream, bypassing the stringent + // alignment tests (we're a bit looser in what we need here, and + // we _know_ we're OK). Then skip the byte order code. + // + CDR stream; + CORBA_TypeCode_ptr tc = 0; + + stream.setup_encapsulation (_buffer, (size_t)_length); + + switch (_kind) { + default: // most have no tc params + break; + + case tk_sequence: // param 0 is a tc + case tk_array: + if (n != 0) + break; + if (CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + return tc; + + case tk_alias: // #1 is a tc + if (n != 2) + break; + if (!stream.skip_string () // type ID, hidden + || !stream.skip_string () // typedef name + || CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + return tc; + + case tk_except: + case tk_struct: // #5 and up are tc, index from 0 + if ((n < 4) || (n & 0x1)) { // tc is at odd number of param list + env.exception (new CORBA_Bounds); + return 0; + } + + if (!stream.skip_string () // type ID, hidden + || !stream.skip_string () // typedef name + || !stream.get_ulong (temp)) { // member count + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } else { + CORBA_ULong i; + temp = (n - 3) / 2; + + // skip member pairs to the one we want + for (i = 0; i < temp; i++) { + // skip to the member being asked + if (!stream.skip_string () // member name + || !skip_typecode (stream)) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + + if (!stream.skip_string () + || CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, + env)!= CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + return tc; + } + + case tk_union: // #6 and up are tc, index from 0 + if (n != 2 && (n < 7 || (n - 7) % 3)) { + env.exception (new CORBA_Bounds); + return 0; + } + + if (!stream.skip_string () // type ID, hidden + || !stream.skip_string () // typedef name + || CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE // TC + ) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } else if (!stream.get_ulong (temp) // default used + || !stream.get_ulong (temp) // member count + ) { + tc->Release (); + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + + if (n == 2) + return tc; + + temp = (n - 7) / 3; + + // + // skip to the member requested + // + CORBA_ULong i; + CORBA_Long scratch; // always big enough + + for (i = 0; i < temp; i++) { + + if (CDR::decoder (tc, &scratch, this, &stream, env) // member label + != CORBA_TypeCode::TRAVERSE_CONTINUE + || !stream.skip_string () // member name + || !skip_typecode (stream)) { // member typecode + tc->Release (); + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + } + + if (CDR::decoder (tc, &scratch, this, &stream, env) // member label + != CORBA_TypeCode::TRAVERSE_CONTINUE + || !stream.skip_string () // member name + ) { + tc->Release (); + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + tc->Release (); + + if (CDR::decoder (_tc_CORBA_TypeCode, &tc, this, &stream, env) + != CORBA_TypeCode::TRAVERSE_CONTINUE) { + env.exception (new CORBA_BAD_TYPECODE (COMPLETED_NO)); + return 0; + } + return tc; + } + + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; +} + + +// +// Return the type ID (RepositoryId) for the TypeCode; it may be empty. +// +// NOTE the string returned here is owned by the typecode!! +// +CORBA_String +CORBA_TypeCode::id ( + CORBA_Environment &env +) const +{ + env.clear (); + + switch (_kind) { + // + // These are all complex typecodes, which have as their first parameter + // (number zero) a repository/type ID string encoded per CDR rules. + // That means we can just return a pointer to that string directly! + // + case tk_objref: + case tk_struct: + case tk_union: + case tk_enum: + case tk_alias: + case tk_except: + return (CORBA_String) (_buffer + + 4 // skip byte order flag and padding + + 4 // skip (strlen + 1) + ); + + // + // No other typecodes ever have type IDs + // + default: + env.exception (new CORBA_BAD_PARAM (COMPLETED_NO)); + return 0; + } +} + diff --git a/TAO/IIOP/test/Makefile b/TAO/IIOP/test/Makefile new file mode 100644 index 00000000000..3dfca9eab2e --- /dev/null +++ b/TAO/IIOP/test/Makefile @@ -0,0 +1,80 @@ +# @(#)Makefile 1.12 95/09/30 +# Makefile for sanity checks + +ROOT = .. + +include $(ROOT)/Makefile.conf + +CPPFLAGS += -I$(ROOT)/proto/include + +LDLIBS = -Qoption ld -R$(ROOT)/proto/lib -L$(ROOT)/proto/lib -lcorba + +PROG_SRCS = svr.cpp clnt.cpp cubit.cpp \ + test1.cpp test1_clnt.cpp test1_svr.cpp \ + echo_clnt.cpp echo_svr.cpp + +TESTS = svr clnt test1_svr test1_clnt +# TESTS = svr clnt test1_svr test1_clnt echo_svr echo_clnt + + +######## +default: $(TESTS) +all: default + + +######## +# Sanity check builds by running basic functionality tests. +# +# "sleep 5" in the server startup is usually enough to get the +# objref into the file so the client can read it. +# +check: $(TESTS) + @echo "testing with 'cube' calls, stub + DII, IOR strings" + @./svr -i30 -o non-internet > obj.1 & sleep 5 + @./clnt -n250 -O `cat obj.1` -x + @echo '' + @echo "testing request forwarding with 'cube' calls, stub + DII" + @./svr -f -i30 > obj.2 & sleep 5 + @./clnt -n250 -O `cat obj.2` -x + @echo '' + @echo "testing transmission of primitive data types" + @./test1_svr -i30 > obj.3 & sleep 5 + @./test1_clnt -n50 -O `cat obj.3` -x + @echo '' +# @echo "testing echo of primitive data values" +# @./echo_svr -i30 > obj.4 & sleep 5 +# @./echo_clnt -O `cat obj.4` -x +# @echo '' + @echo "testing with 'cube' calls, MT-ized (no forwarding)" + @./svr -t -i30 -o non-internet > obj.5 & sleep 5 + @./clnt -n250 -O `cat obj.5` -x + @echo '' + +######## +# CUBIT test +svr: svr.o cubit.o + $(LINK.cc) -o svr svr.o cubit.o $(LDLIBS) +clnt: cubit.o clnt.o + $(LINK.cc) -o clnt clnt.o cubit.o $(LDLIBS) + +######## +# BASIC DATATYPES test +test1_clnt: test1.o test1_clnt.o + $(LINK.cc) -o test1_clnt test1_clnt.o test1.o $(LDLIBS) +test1_svr: test1.o test1_svr.o + $(LINK.cc) -o test1_svr test1_svr.o test1.o $(LDLIBS) + +######## +# ECHO test ... "test1" where the operation semantics are violated; +# this aids some porting work, but is a less rigorous test +echo_clnt: test1.o echo_clnt.o + $(LINK.cc) -o echo_clnt echo_clnt.o test1.o $(LDLIBS) +echo_svr: test1.o echo_svr.o + $(LINK.cc) -o echo_svr echo_svr.o test1.o $(LDLIBS) + +clean: + -rm -rf *.o Log $(TESTS) obj.* core Templates.DB .make.state + +install: + -@echo "Nothing to install, these are tests!" + diff --git a/TAO/IIOP/test/clnt.cpp b/TAO/IIOP/test/clnt.cpp new file mode 100644 index 00000000000..28a6739f01e --- /dev/null +++ b/TAO/IIOP/test/clnt.cpp @@ -0,0 +1,491 @@ +// @(#)clnt.cpp 1.2 95/09/12 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TEST: Simple "cube" client, calling hand-crafted stubs. +// + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#if unix +# include <unistd.h> +# include <sys/time.h> + +#else // windows +# include "getopt.h" // e.g. GNU's version + +#endif // unix + +#include "cubit.hh" + +#include "../lib/runtime/debug.hh" + + +#if !defined (DECLARED_GETTIMEOFDAY) +extern "C" int gettimeofday (struct timeval *, struct timezone *); +#endif + +extern char *optarg; // missing on some platforms + +inline int func (unsigned i) { return i - 117; } + +extern void +print_exception (const CORBA_Exception *, const char *, FILE *f=stdout); + + +// +// forward declarations +// +static void cube_union_stub(unsigned, unsigned&, unsigned&, + CORBA_Object_ptr, CORBA_Environment &); + +static void cube_union_dii(unsigned &, unsigned &, + CORBA_Object_ptr, CORBA_Environment &); + + +int +main (int argc, char *const *argv) +{ + CORBA_ORB_ptr orb_ptr; + CORBA_Environment env; + CORBA_Object_ptr objref = CORBA_Object::_nil(); + unsigned loop_count = 1; + int exit_later = 0; + + orb_ptr = CORBA_ORB_init (argc, argv, "internet", env); + if (env.exception () != 0) { + print_exception (env.exception (), "ORB initialisation"); + return 1; + } + + // + // Parse command line and verify parameters. + // + int c; + + while ((c = getopt (argc, argv, "dn:O:x")) != EOF) + switch (c) { + case 'd': // debug flag + debug_level++; + continue; + + case 'n': // loop count + loop_count = (unsigned) atoi (optarg); + continue; + + case 'O': // stringified objref + { + objref = orb_ptr->string_to_object ( + (CORBA_String)optarg, env); + if (env.exception () != 0) { + print_exception (env.exception (), "string2object"); + return 1; + } + } + continue; + + case 'x': + exit_later++; + continue; + + case '?': + default: + fprintf (stderr, "usage: %s" + " [-d]" + " [-n loopcount]" + " [-O objref]" + " [-x]" + "\n", argv [0] + ); + return 1; + } + + if (CORBA_is_nil (objref) == CORBA_B_TRUE) { + fprintf (stderr, "%s: must identify non-null target objref\n", + argv [0]); + return 1; + } + + // + // See if the type of the objref is correct ... and while we're + // at it, incur connection setup costs outside of the timing loop. + // (Should probably report setup costs.) + // + CORBA_Boolean type_ok; + + type_ok = objref->_is_a (Cubit__id, env); + if (env.exception () != 0) { + print_exception (env.exception (), "check type of target"); + return -1; + } else if (type_ok != CORBA_B_TRUE) { + fprintf (stderr, "%s: target objref is of wrong type\n", + argv [0]); + printf ("type_ok = %d\n", type_ok); + return 1; + } + + // + // Make the calls in a loop. + // + // XXX should do two things: (a) put all the calls into a + // separate routine, and (b) set it up so a utility routine + // loops/times the calls ... these will allow (c) adding + // more categories of calls + // + unsigned i; + unsigned call_count, error_count; + + call_count = 0; + error_count = 0; + +#if defined (HAVE_GETTIMEOFDAY) + timeval before, after; + + if (gettimeofday (&before, 0) < 0) + dperror ("gettimeofday before"); +#endif // defined (HAVE_GETTIMEOFDAY) + + for (i = 0; i < loop_count; i++) { + // + // Cube an octet. + // + CORBA_Octet arg_octet, ret_octet; + + call_count++; + ret_octet = Cubit_cube_octet (objref, arg_octet = func (i), env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_octet"); + error_count++; + } else { + dmsg2 ("cube octet: %d --> %d\n", arg_octet, ret_octet); + arg_octet = arg_octet * arg_octet * arg_octet; + if (arg_octet != ret_octet) { + printf ("** cube_octet(%d) ERROR (--> %d)\n", + (CORBA_Octet) func (i), ret_octet); + error_count++; + } + } + + // + // Cube a short. + // + CORBA_Short arg_short, ret_short; + + call_count++; + ret_short = Cubit_cube_short (objref, arg_short = func (i), env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_short"); + error_count++; + } else { + dmsg2 ("cube short: %d --> %d\n", arg_short, ret_short); + arg_short = arg_short * arg_short * arg_short; + if (arg_short != ret_short) { + printf ("** cube_short(%d) ERROR (--> %d)\n", + (CORBA_Short) func (i), ret_short); + error_count++; + } + } + + // + // Cube a long. + // + CORBA_Long arg_long, ret_long; + + call_count++; + ret_long = Cubit_cube_long (objref, arg_long = func (i), env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_long"); + error_count++; + } else { + dmsg2 ("cube long: %d --> %d\n", arg_long, ret_long); + arg_long = arg_long * arg_long * arg_long; + if (arg_long != ret_long) { + printf ("** cube_long(%ld) ERROR (--> %ld)\n", + (CORBA_Long) func (i), ret_long); + error_count++; + } + } + + // + // Cube a "struct" ... + // + Cubit_Many arg_struct, *ret_struct; + + call_count++; + + arg_struct.l = func (i); + arg_struct.s = func (i); + arg_struct.o = func (i); + + ret_struct = Cubit_cube_struct (objref, arg_struct, env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_struct"); + error_count++; + } else { + dmsg ("cube struct ..."); + arg_struct.l = arg_struct.l * arg_struct.l * arg_struct.l; + arg_struct.s = arg_struct.s * arg_struct.s * arg_struct.s; + arg_struct.o = arg_struct.o * arg_struct.o * arg_struct.o; + + if (arg_struct.l != ret_struct->l + || arg_struct.s != ret_struct->s + || arg_struct.o != ret_struct->o) { + printf ("** cube_struct ERROR\n"); + error_count++; + } + delete ret_struct; + } + + } + +#if defined (HAVE_GETTIMEOFDAY) + if (gettimeofday (&after, 0) < 0) + dperror ("gettimeofday after"); + + if (call_count > 0) { + if (error_count == 0) { + unsigned long us; + + us = after.tv_sec - before.tv_sec; + us *= 1000 * 1000; + us += after.tv_usec - before.tv_usec; + us /= call_count; + + printf ("cube average call time\t= %ld.%.03ldms, \t" + "%ld calls/second\n", + us / 1000, us % 1000, + 1000000L / us); + } + + printf ("%d calls, %d errors\n", call_count, error_count); + } +#endif // defined (HAVE_GETTIMEOFDAY) + + // + // Simple test for DII: call "cube_struct". (It's not timed + // since the copious mallocation of DII would bias numbers against + // typical stub-based calls.) + // + do { + // + // Create the request ... + // + CORBA_Request_ptr req; + + req = objref->_request ((const CORBA_String) "cube_struct", env); + if (env.exception () != 0) { + print_exception (env.exception (), "DII request create"); + break; + } + + // + // ... initialise the argument list and result ... + // + Cubit_Many arg, *result; + + arg.o = 3; arg.l = 5; arg.s = -7; + + CORBA_Any tmp_arg (TC_Cubit_Many, &arg, CORBA_B_FALSE); + + req->arguments ()->add_value (0, tmp_arg, CORBA_ARG_IN, env); + if (env.exception () != 0) { + print_exception (env.exception (), "DII request arg add"); + CORBA_release (req); + break; + } + + req->result ()->value () + ->replace (TC_Cubit_Many, 0, CORBA_B_TRUE, env); + if (env.exception () != 0) { + print_exception (env.exception (), "DII request result type"); + CORBA_release (req); + break; + } + + // + // Make the invocation, verify the result + // + req->invoke (); + if (req->env ()->exception () != 0) { + print_exception (req->env ()->exception (), "DII invoke"); + CORBA_release (req); + break; + } + + result = (Cubit_Many *) req->result ()->value ()->value (); + + if (result->o != 27 || result->l != 125 || result->s != -343) + fprintf (stderr, "DII cube_struct -- bad results\n"); + else + dmsg ("DII cube_struct ... success!!"); + + CORBA_release (req); + + } while (0); + + // + // Two more tests, using the "cube_union" function + // + cube_union_dii(call_count, error_count, objref, env); + if (env.exception () != 0) + error_count++; + + cube_union_stub(i, call_count, error_count, objref, env); + if (env.exception () != 0) + error_count++; + + if (exit_later) { + Cubit_please_exit (objref, env); + dexc (env, "server, please exit"); + } + + CORBA_release (objref); + + return (error_count == 0) ? 0 : 1; +} + + +static void +cube_union_stub( + unsigned i, + unsigned &call_count, + unsigned &error_count, + CORBA_Object_ptr objref, + CORBA_Environment &env) +{ + // + // Cube a "union" ... + // + Cubit_oneof u, *r; + + call_count++; + + u._disc = e_2nd; + u.l = 3; + + r = Cubit_cube_union (objref, u, env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_union"); + error_count++; + } else { + dmsg ("cube union ..."); + u.l = u.l * u.l * u.l ; + + if (u.l != r->l) { + printf ("** cube_union ERROR\n"); + error_count++; + } + + delete r; + } + + // + // Cube another "union" which uses the default arm ... + // + call_count++; + + u._disc = e_5th; + u.cm.l = func (i); + u.cm.s = func (i); + u.cm.o = func (i); + + u.cm.l = 7; + u.cm.s = 5; + u.cm.o = 3; + + r = Cubit_cube_union (objref, u, env); + if (env.exception () != 0) { + print_exception (env.exception (), "from cube_union"); + error_count++; + } else { + dmsg ("cube union ..."); + u.cm.l = u.cm.l * u.cm.l * u.cm.l; + u.cm.s = u.cm.s * u.cm.s * u.cm.s; + u.cm.o = u.cm.o * u.cm.o * u.cm.o; + + if (u.cm.l != r->cm.l + || u.cm.s != r->cm.s + || u.cm.o != r->cm.o) { + printf ("** cube_union ERROR\n"); + error_count++; + } + + delete r; + } +} + + +static void +cube_union_dii ( + unsigned &call_count, + unsigned &error_count, + CORBA_Object_ptr objref, + CORBA_Environment &env) +{ + // + // Create the request ... + // + CORBA_Request_ptr req; + + call_count++; + + req = objref->_request ((const CORBA_String) "cube_union", env); + if (env.exception () != 0) { + error_count++; + + print_exception (env.exception (), "cube_union_dii request create"); + return; + } + + // + // ... initialise the argument list and result ... + // + Cubit_oneof u, *r; + + u._disc = e_3rd; + u.cm.l = 5; + u.cm.s = -7; + u.cm.o = 3; + + CORBA_Any tmp_arg (TC_Cubit_oneof, &u, CORBA_B_FALSE); + + req->arguments ()->add_value (0, tmp_arg, CORBA_ARG_IN, env); + if (env.exception () != 0) { + error_count++; + print_exception (env.exception (), "cube_union_dii request arg add"); + CORBA_release (req); + return; + } + + req->result ()->value ()->replace (TC_Cubit_oneof, 0, CORBA_B_TRUE, env); + if (env.exception () != 0) { + error_count++; + print_exception (env.exception (), "cube_union_dii result type"); + CORBA_release (req); + return; + } + + // + // Make the invocation, verify the result + // + req->invoke (); + if (req->env ()->exception () != 0) { + error_count++; + print_exception (req->env ()->exception (),"cube_union_dii invoke"); + CORBA_release (req); + return; + } + + r = (Cubit_oneof *) req->result ()->value ()->value (); + + if (r->cm.o != 27 || r->cm.l != 125 || r->cm.s != -343) { + error_count++; + fprintf (stderr, "cube_union_dii -- bad results\n"); + } + else + dmsg ("cube_union_dii ... success!!"); + + CORBA_release (req); +} diff --git a/TAO/IIOP/test/cubit.cpp b/TAO/IIOP/test/cubit.cpp new file mode 100644 index 00000000000..077ba286522 --- /dev/null +++ b/TAO/IIOP/test/cubit.cpp @@ -0,0 +1,590 @@ +// @(#)cubit.cpp 1.2 95/09/29 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TEST: hand-written C-style "Cubit" stubs and "skeletons" +// +// NOTE: these "skeletons" are really the methods, using DSI. No real +// ORB would be implemented in this particular way. Several things would +// be more typical of real (static) skeletons: +// +// * Most of the "in" (and much of the "out") parameter data would +// be preallocated on the stack, not heap allocated. (Static +// preallocation doesnt' really work in a multithreaded system, +// and moreover can waste a lot of space.) +// +// * The ORB core wouldn't be told about parameters using heap +// allocated data structures (e.g. NVList). +// +// * Skeletons would need to some kind of "marshal the response NOW" +// API so that stack-allocated "out" values wouldn't become invalid +// up until they were safely marshaled. +// +// * They'd handle exceptions rather than just generating debugging +// messages when they happen. +// +// * Method code would be called by the skeletons, not written as +// part of the "skeleton" itself! +// +// A key part of turning this code into a complete ORB would be to ensure +// that skeletons were always efficient and correct. They might not need +// to be sharable between different implementations of the same OMG-IDL +// object interface, but many ORBs choose to be structured that way. +// + +#include "cubit.hh" // for stubs ... +#include <corba/toa.hh> // ... and skeletons + +#include "../lib/runtime/debug.hh" // ... and debugging + + +// +// CUBE OCTET +// + +static const paramdata Cubit_cube_octet_params [] = { + { _tc_CORBA_Octet, PARAM_RETURN, 0 }, + { _tc_CORBA_Octet, PARAM_IN, 0 } +}; + +static const calldata Cubit_cube_octet_calldata = { + "cube_octet", CORBA_B_TRUE, + 2, &Cubit_cube_octet_params [0], + 0, 0 +}; + + +CORBA_Octet +Cubit_cube_octet ( + Cubit_ptr target, + CORBA_Octet o, + CORBA_Environment &env +) +{ + CORBA_Octet retval; + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_cube_octet_calldata, + &retval, &o); + data->Release (); + } + return retval; +} + +static void +_cube_octet_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (_tc_CORBA_Octet); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + dexc (env, "cube_octet, add value"); + + req.params (nvlist, env); + dexc (env, "cube_octet, get params"); + + CORBA_Octet *value = new CORBA_Octet; + + *value = *(CORBA_Octet *)nv->value ()->value (); + // dmsg1 ("cube octet, parameter '%d'", *value); + *value = (CORBA_Octet) ((*value) * (*value) * (*value)); + // dmsg1 ("cube octet, result '%d'", *value); + + CORBA_Any *any = + new CORBA_Any (_tc_CORBA_Octet, value, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "cube_octet, result"); +} + + +// +// CUBE SHORT +// + +static const paramdata Cubit_cube_short_params [] = { + { _tc_CORBA_Short, PARAM_RETURN, 0 }, + { _tc_CORBA_Short, PARAM_IN, 0 } +}; + +static const calldata Cubit_cube_short_calldata = { + "cube_short", CORBA_B_TRUE, + 2, &Cubit_cube_short_params [0], + 0, 0 +}; + + +CORBA_Short +Cubit_cube_short ( + Cubit_ptr target, + CORBA_Short s, + CORBA_Environment &env +) +{ + CORBA_Short retval; + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_cube_short_calldata, + &retval, &s); + data->Release (); + } + return retval; +} + +static void +_cube_short_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (_tc_CORBA_Short); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + dexc (env, "cube_short, add_value"); + + req.params (nvlist, env); + dexc (env, "cube_short, get params"); + + CORBA_Short *value = new CORBA_Short; + + *value = *(CORBA_Short *)nv->value ()->value (); + // dmsg1 ("cube short, parameter '%d'", *value); + *value =(CORBA_Short) ((*value) * (*value) * (*value)); + // dmsg1 ("cube short, result '%d'", *value); + + CORBA_Any *any = + new CORBA_Any (_tc_CORBA_Short, value, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "cube_short, result"); +} + + +// +// CUBE LONG +// + +static const paramdata Cubit_cube_long_params [] = { + { _tc_CORBA_Long, PARAM_RETURN, 0 }, + { _tc_CORBA_Long, PARAM_IN, 0 } +}; + +static const calldata Cubit_cube_long_calldata = { + "cube_long", CORBA_B_TRUE, + 2, &Cubit_cube_long_params [0], + 0, 0 +}; + + +CORBA_Long +Cubit_cube_long ( + Cubit_ptr target, + CORBA_Long l, + CORBA_Environment &env +) +{ + CORBA_Long retval; + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_cube_long_calldata, + &retval, &l); + data->Release (); + } + return retval; +} + + +static void +_cube_long_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (_tc_CORBA_Long); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + dexc (env, "cube_long, add_value"); + + req.params (nvlist, env); + dexc (env, "cube_long, get params"); + + CORBA_Long *value = new CORBA_Long; + + *value = *(CORBA_Long *)nv->value ()->value (); + // dmsg1 ("cube long, parameter '%d'", *value); + *value = (*value) * (*value) * (*value); + // dmsg1 ("cube long, result '%d'", *value); + + CORBA_Any *any = + new CORBA_Any (_tc_CORBA_Long, value, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "cube_long, result"); +} + +// +// Encapsulated parameters for struct "Cubit_Many" typecode. +// None of these parameters is complicated, so this is just +// a linear sequence of element encodings +// +// NOTE: it's important that this be longword aligned!! +// +static const CORBA_Long _oc_Cubit_Many [] = { + 1, // byte order flag (TRICKY!) + + 1, 0, // empty string: repository/type ID + 1, 0, // empty string: struct name + + 3, // three struct elements + + // First structure element: name, typecode for Octet + 1, 0, // empty string: name "o" + tk_octet, + + // Second structure element: name, typecode for Long + 1, 0, // empty string: name "l" + tk_long, + + // Third structure element: name, typecode for Short + 1, 0, // empty string: name "s" + tk_short, +}; + +static CORBA_TypeCode _tc_Cubit_Many (tk_struct, + sizeof _oc_Cubit_Many, (unsigned char *) &_oc_Cubit_Many, + CORBA_B_FALSE); +CORBA_TypeCode_ptr TC_Cubit_Many = &_tc_Cubit_Many; + + +// +// CUBE STRUCT +// + +static const paramdata Cubit_cube_struct_params [] = { + { &_tc_Cubit_Many, PARAM_RETURN, sizeof (Cubit_Many) }, + { &_tc_Cubit_Many, PARAM_IN, 0 } +}; + +static const calldata Cubit_cube_struct_calldata = { + "cube_struct", CORBA_B_TRUE, + 2, &Cubit_cube_struct_params [0], + 0, 0 +}; + +Cubit_Many * +Cubit_cube_struct ( + Cubit_ptr target, + Cubit_Many &values, + CORBA_Environment &env +) +{ + Cubit_Many *retval; + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_cube_struct_calldata, + &retval, &values); + data->Release (); + } + return retval; +} + + +static void +_cube_struct_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (TC_Cubit_Many); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + dexc (env, "cube_struct, add_value"); + + req.params (nvlist, env); + dexc (env, "cube_struct, get params"); + + Cubit_Many *value; + Cubit_Many *retval = new Cubit_Many; + + value = (Cubit_Many *)nv->value ()->value (); + + retval->o = (CORBA_Octet) (value->o * value->o * value->o); + retval->s = (CORBA_Short) (value->s * value->s * value->s); + retval->l = value->l * value->l * value->l; + + // dmsg2 ("cube struct.o, %d -> %d", value->o, retval->o); + // dmsg2 ("cube struct.s, %d -> %d", value->s, retval->s); + // dmsg2 ("cube struct.l, %d -> %d", value->l, retval->l); + + CORBA_Any *any = + new CORBA_Any (TC_Cubit_Many, retval, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "cube_struct, result"); +} + +// +// CUBE UNION +// + +// +// NOTE: not all union typecodes can be encoded as an array +// of "long "values, but this one can. Ones with discriminants +// that are one or two bytes long can't easily be coded portably. +// +// The benefit of doing it as an array of "long" values is +// twofold: (a) easier to read; (b) on most systems it's then +// adequately aligned for the typecode interpreter to use, so +// no additional runtime copy needs to be made. +// +static const CORBA_Long _oc_Cubit_oneof [] = { + 1, // byte order flag (TRICKY) + 1, 0, // omitted repository/type ID + 1, 0, // omitted struct name, "oneof" + + // + // discriminant typecode: + // + tk_enum, // tk_enum + 72, // encapsulation length + + 1, // byte order flag (TRICKY) + 1, 0, // omitted repository/type ID + 1, 0, // omitted enum name, "discrim" + 6, // 5 elements in the enum + + 1, 0, // omitted member name, "e_0th" + 1, 0, // omitted member name, "e_1st" + 1, 0, // omitted member name, "e_2nd" + 1, 0, // omitted member name, "e_3rd" + 1, 0, // omitted member name, "e_4th" + 1, 0, // omitted member name, "e_5th" + + 4, // default member index (zero based) + 5, // number of union members + + // the 1st union branch arm + e_0th, // member label value + 1, 0, // omitted member name, "o" + tk_octet, // member typecode + + // the 2nd union branch arm + e_1st, // member label value + 1, 0, // omitted member name, "s" + tk_short, // member typecode + + // the 3rd union branch arm + e_2nd, // member label value + 1, 0, // omitted member name, "l" + tk_long, // member typecode + + // the 4th union branch arm + e_3rd, // member label value + 1, 0, // omitted member name, "cm" + + // the 4th union member typecode + tk_struct, // tk_struct + 60, // encap length + + 1, // byte order flag (TRICKY) + 1, 0, // omitted repository/type ID + 1, 0, // omitted struct name, "Many" + 3, // three struct members + + // First structure element + 1, 0, // omitted member name, "o" + tk_octet, // member type, tk_octet + + // Second structure element + 1, 0, // omitted member name, "l" + tk_long, // member type, tk_long + + // Third structure element + 1, 0, // omitted member name, "s" + tk_short, // member type, tk_short + + // the 5th union branch arm + 4, // the 5th member label value + 1, 0, // omitted member name, "cm" + ~0, // indirected typecode (~0) + -84 // offset to struct "Many" typecode +}; + +static CORBA_TypeCode _tc_Cubit_oneof (tk_union, + (sizeof _oc_Cubit_oneof), (unsigned char *) &_oc_Cubit_oneof, + CORBA_B_FALSE); +CORBA_TypeCode_ptr TC_Cubit_oneof = &_tc_Cubit_oneof; + +static const paramdata Cubit_cube_union_params [] = { + { &_tc_Cubit_oneof, PARAM_RETURN, sizeof (Cubit_oneof) }, + { &_tc_Cubit_oneof, PARAM_IN, 0 } +}; + +static const calldata Cubit_cube_union_calldata = { + "cube_union", CORBA_B_TRUE, + 2, &Cubit_cube_union_params [0], + 0, 0 +}; + +Cubit_oneof * +Cubit_cube_union ( + Cubit_ptr target, + Cubit_oneof &values, + CORBA_Environment &env +) +{ + Cubit_oneof *retval; + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_cube_union_calldata, + &retval, &values); + data->Release (); + } + return retval; +} + + +static void +_cube_union_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (TC_Cubit_oneof); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + dexc (env, "cube_union_3rd, add_value"); + + req.params (nvlist, env); + dexc (env, "cube_union_3rd, get params"); + + Cubit_oneof *v; + Cubit_oneof *r = new Cubit_oneof; + + v = (Cubit_oneof *)nv->value ()->value (); + r->_disc = v->_disc; + + switch (v->_disc) { + case e_0th: + r->o = (CORBA_Octet) (v->o * v->o * v->o); + break; + + case e_1st: + r->s = (CORBA_Short) (v->s * v->s * v->s); + break; + + case e_2nd: + r->l = v->l * v->l * v->l; + break; + + case e_3rd: + default: + r->cm.o = (CORBA_Octet) (v->cm.o * v->cm.o * v->cm.o); + r->cm.s = (CORBA_Short) (v->cm.s * v->cm.s * v->cm.s); + r->cm.l = v->cm.l * v->cm.l * v->cm.l; + break; + } + + CORBA_Any *any = new CORBA_Any (TC_Cubit_oneof, r, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "cube_struct, result"); +} + + +// +// PLEASE EXIT +// + +static const calldata Cubit_please_exit_calldata = { + "please_exit", CORBA_B_FALSE, + 0, 0, + 0, 0 +}; + +void +Cubit_please_exit ( + Cubit_ptr target, + CORBA_Environment &env +) +{ + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &Cubit_please_exit_calldata + ); + data->Release (); + } +} + +static void +_please_exit_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + dmsg ("I've been asked to shut down..."); + req.oa ()->please_shutdown (env); + dexc (env, "please_exit, please_shutdown"); +} + + +const CORBA_Char *Cubit__id = (CORBA_Char *) + "IDL:Eng.SUN.COM/Cubit:1.1"; + + +// +// table of all operations, used by operation dispatch to get to the +// right skeleton ... could be sorted by the IDL compiler so bsearch +// is effective, perhaps with help from opname hashes and a small cache +// (e.g. like Obj-C?). for now, just lsearch. +// +const skel_entry Cubit_operations [] = { + { &Cubit_cube_octet_calldata, _cube_octet_skel }, + { &Cubit_cube_short_calldata, _cube_short_skel }, + { &Cubit_cube_long_calldata, _cube_long_skel }, + { &Cubit_cube_struct_calldata, _cube_struct_skel }, + { &Cubit_cube_union_calldata, _cube_union_skel }, + { &Cubit_please_exit_calldata, _please_exit_skel }, + { 0, 0 } // last entry +}; diff --git a/TAO/IIOP/test/cubit.hh b/TAO/IIOP/test/cubit.hh new file mode 100644 index 00000000000..aae2c06bd31 --- /dev/null +++ b/TAO/IIOP/test/cubit.hh @@ -0,0 +1,106 @@ +// @(#)cubit.hh 1.1 95/09/10 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// Hand-crafted C language binding glue ... +// +// This doesn't use C++ since doing the obvious derivation +// (all parent interfaces are virtual public parents) makes +// object references have different "views". That is, a +// pointer to a Cubit (i.e. a Cubit_ptr) would not have +// the same binary value as a pointer to a CORBA_Object +// (i.e. a CORBA_Object_ptr, generic objref). That'd +// mean lots of narrowing/widening/RTTI infrastructure. +// + +#ifndef _CUBIT_HH +#define _CUBIT_HH + +#ifdef _MSC_VER +#pragma pack (push, 1) // VC++, known padding rules +#endif // VC++ + +#include <corba/orb.hh> +#include <corba/stub.hh> + + +// +// C style binding +// + +typedef CORBA_Object Cubit; +typedef Cubit *Cubit_ptr, *CubitRef; + +extern CORBA_TypeCode_ptr TC_Cubit_Many; +extern CORBA_TypeCode_ptr TC_Cubit_oneof; + +struct Cubit_Many { + CORBA_Octet o; + CORBA_Long l; + CORBA_Short s; +}; + +enum Cubit_discrim {e_0th = 0, e_1st = 1, e_2nd = 2, + e_3rd = 3, e_4th = 4, e_5th = 5}; + +struct Cubit_oneof { + Cubit_discrim _disc; + + union { + CORBA_Octet o; + CORBA_Short s; + CORBA_Long l; + Cubit_Many cm; + }; +}; + +CORBA_Octet +Cubit_cube_octet ( + Cubit_ptr target, + CORBA_Octet o, + CORBA_Environment &env +); + +CORBA_Short +Cubit_cube_short ( + Cubit_ptr target, + CORBA_Short s, + CORBA_Environment &env +); + +CORBA_Long +Cubit_cube_long ( + Cubit_ptr target, + CORBA_Long l, + CORBA_Environment &env +); + +Cubit_Many * +Cubit_cube_struct ( + Cubit_ptr target, + Cubit_Many &values, + CORBA_Environment &env +); + +Cubit_oneof * +Cubit_cube_union ( + Cubit_ptr target, + Cubit_oneof &values, + CORBA_Environment &env +); + +void +Cubit_please_exit ( + Cubit_ptr target, + CORBA_Environment &env +); + +extern const CORBA_Char *Cubit__id; // type ID + +extern const skel_entry Cubit_operations []; + +#ifdef _MSC_VER +#pragma pack (pop) // VC++, go back to other padding rules +#endif // VC++ + +#endif // _CUBIT_HH diff --git a/TAO/IIOP/test/cubit.idl b/TAO/IIOP/test/cubit.idl new file mode 100644 index 00000000000..d9b38c34a2a --- /dev/null +++ b/TAO/IIOP/test/cubit.idl @@ -0,0 +1,40 @@ +// @(#)cubit.idl 1.1 95/09/10 +// Copyright 1994-1995 by Sun Microsystems, Inc. + +#pragma prefix "Eng.SUN.COM" +#pragma version Cubit 1.1 + +interface Cubit { + octet cube_octet (in octet o); + short cube_short (in short s); + long cube_long (in long l); + + struct Many { + octet o; // + 3 bytes padding (normally) ... + long l; + short s; // + 2 bytes padding (normally) ... + }; + + Many cube_struct (in Many values); + + enum discrim {e_0th, e_1st, e_2nd, e_3rd, e_4th, e_5th}; + + union oneof + switch (discrim) { + // this is an easy union to interpret; no padding + // is needed between discriminant and value. + case e_0th: + octet o; + case e_1st: + short s; + case e_2nd: + long l; + case e_3rd: + default: + Many cm; + }; + + oneof cube_union (in oneof values); + + oneway void please_exit (); +}; diff --git a/TAO/IIOP/test/svr.cpp b/TAO/IIOP/test/svr.cpp new file mode 100644 index 00000000000..cb34263c82c --- /dev/null +++ b/TAO/IIOP/test/svr.cpp @@ -0,0 +1,423 @@ +// @(#)svr.cpp 1.6 95/10/02 +// Copyright 1994-1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TEST: simple IIOP server for "cubit.idl" interface. +// +// Starts up, builds an objref, prints its string, listens for +// messages, responds to them. +// + +#include <stdio.h> +#include <string.h> + +#if unix +# include <unistd.h> // for getopt on some systems + +#else // windows +# include "getopt.h" // e.g. GNU's version + +#endif + +#include "cubit.hh" +#include <corba/toa.hh> + + +// +// XXX a general debug/trace facility would be handy +// +#include "../lib/runtime/debug.hh" + +// +// XXX this stuff is ugly but needed, since this exposes features +// (IIOP forwarding) that TOA doesn't provide. +// +#include "../lib/bridge/connmgr.hh" +#include "../lib/bridge/tcpoa.hh" + + +extern char *optarg; // missing on some platforms + +extern void +print_exception (const CORBA_Exception *, const char *, FILE *f=stdout); + + +static void +is_a_skel ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (_tc_CORBA_String); + + req.orb()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + + req.params (nvlist, env); + if (env.exception () != 0) { + dexc (env, "is_a_skel, get params"); + return; + } + + CORBA_Boolean *retval; + CORBA_String value = *(CORBA_String *) + nv->value ()->value (); + + // + // This object's type "is_a" conformant subtype of the type whose + // ID ("Repository ID") is passed if it's (a) either the same type + // as indicated by the ID, or (b) one of the classes from which it + // inherits is_a subtype, or (c) the ID is for the generic CORBA + // object. + // + // XXX IDs should be compared recognizing that the "IDL" and "DCE" + // ID types have a minor version code, and that if the minor code + // (a 16 bit unsigned integer, in ASCII form) of the ID passed is + // not greater than one of the IDs we know, it's still compatible. + // + // XXX "env" should be checked to see if TypeCode::id() reported + // an exception ... + // + if (strcmp ((char *)value, (char *)Cubit__id) == 0 + || strcmp ((char *)value, _tc_CORBA_Object->id (env)) == 0) + retval = new CORBA_Boolean (CORBA_B_TRUE); + else + retval = new CORBA_Boolean (CORBA_B_FALSE); + + CORBA_Any *any = + new CORBA_Any (_tc_CORBA_Boolean, retval, CORBA_B_TRUE); + + req.result (any, env); + dexc (env, "_is_a, result"); +} + + +// +// Dispatch to Skeletons +// +// XXX explore packaging most of this as part of the TCP_OA !! +// +static void +tcpoa_dispatch ( + CORBA_OctetSeq &key, + CORBA_ServerRequest &req, + void *context, + CORBA_Environment &env +) +{ + // + // Verify that the target object and "this" object have the + // same key. Normally, this would be used to figure out + // which object was the target, and hence which operations + // vector to dispatch the request. + // + CORBA_OctetSeq *obj_key; + + obj_key = (CORBA_OctetSeq *) context; + + if (obj_key->length != key.length + || memcmp (obj_key->buffer, key.buffer, + obj_key->length) != 0) { + env.exception (new CORBA_OBJECT_NOT_EXIST (COMPLETED_NO)); +#ifdef DEBUG + if (debug_level) + dmsg_opaque ("request to nonexistent object, key = ", + key.buffer, key.length); +#endif + return; + } + + // + // Find a "skeleton" (nyet :-) entry for this operation, + // then call it with the right per-object state. (Someday this + // search will be sped up, e.g. by hashing or binary search.) + // + const skel_entry *entry; + CORBA_String opname; + + opname = req.op_name (); + + for (entry = &Cubit_operations [0]; entry->op_descriptor; entry++) { + if (strcmp ((char *)opname, entry->op_descriptor->opname) == 0) { + entry->impl_skeleton (req, env); + return; + } + } + + // + // Try one of the ORB's built-in operations. + // + // XXX the rest too: _non_existent (just return false), + // _get_interface (needs an interface repository reference for this + // objref's type), and _get_implementation (needs an implementation + // repository). + // + if (strcmp ((char *)opname, "_is_a") == 0) { + is_a_skel (req, env); + return; + } + + // + // No match. Operation not implemented; say so. + // + dmsg1 ("unknown operation, %s", opname); + env.exception (new CORBA_BAD_OPERATION (COMPLETED_NO)); +} + +// +// forwarding support +// +static CORBA_Object_ptr fwd_ref; + +static void +tcpoa_forwarder ( + CORBA_OctetSeq &key, + CORBA_Object_ptr &the_ref, + void *context, + CORBA_Environment &env +) +{ + CORBA_OctetSeq *obj_key; + + obj_key = (CORBA_OctetSeq *) context; + + if (obj_key->length == key.length + && memcmp (obj_key->buffer, key.buffer, key.length) == 0) { + the_ref = fwd_ref->_duplicate (fwd_ref); + } else + env.exception (new CORBA_OBJECT_NOT_EXIST (COMPLETED_NO)); +} + + +// +// Socket-based passive OA entry point +// +int +OA_listen ( + CORBA_ORB_ptr orb_ptr, + TCP_OA_ptr oa_ptr, + CORBA_String key, + int idle, + CORBA_Boolean do_fork, + CORBA_Boolean do_threads +) +{ + // + // Create the object we'll be implementing. + // + CORBA_OctetSeq obj_key; + CORBA_Object_ptr obj; + CORBA_Environment env; + + obj_key.buffer = (CORBA_Octet *) key; + obj_key.length = obj_key.maximum = strlen ((char *)key); + + obj = oa_ptr->create (obj_key, (CORBA_String) "", env); + if (env.exception () != 0) { + print_exception (env.exception (), "TCP_OA::create"); + return 1; + } + + // + // Stringify the objref we'll be implementing, and + // print it to stdout. Someone will take that string + // and give it to some client. Then release the object. + // + CORBA_String str; + + str = orb_ptr->object_to_string (obj, env); + if (env.exception () != 0) { + print_exception (env.exception (), "object2string"); + return 1; + } + puts ((char *)str); + fflush (stdout); + dmsg1 ("listening as object '%s'", str); + CORBA_release (obj); + obj = 0; + + // + // If we're forking a child server, do so -- read the objref + // it'll use, and prepare to forward all requests to it. That + // objref has a dynamically assigned port. + // + if (do_fork) { +#if defined (HAVE_POPEN) + FILE *f = popen ("exec ./svr -i120 -kbaskerville", "r"); + char buffer [BUFSIZ]; + + if (fgets (buffer, sizeof buffer, f) != buffer) { + fprintf (stderr, "error: can't read from child\n"); + return 1; + } + fwd_ref = orb_ptr->string_to_object ((CORBA_String) buffer, env); + if (env.exception () != 0) { + print_exception (env.exception (), "string2object"); + return 1; + } + + // + // NOTE: don't fclose("f") since some systems make that the + // same as pclose("f"). Pclose waits for the child to exit, + // causing a deadlock since the child won't exit until it's + // told to do so by a client, but no client can be redirected + // to the child until the pclose returns ... + // +#else + fprintf (stderr, "error: no popen(), can't create child\n"); + env.exception (new CORBA_IMP_LIMIT); + return 1; +#endif // !defined (HAVE_POPEN) + } + + // + // Handle requests for this object until we're killed, or one of + // the methods asks us to exit. + // + // NOTE: for multithreaded environments (e.g. POSIX threads) also + // want to use threads. The current notion is to dedicate a thread + // to a "read" on each client file descriptor, and then when that + // successfully gets a Request message, to start another thread + // reading that descriptor while the first one creates the Reply. + // + // This will accentuate the need for server-side policies to address + // resource management, such as shutting down connections that have + // no requests in progress after they've been idle for some time + // period (e.g. 10 minutes), and reclaiming the thread used by that + // connection. + // + while (oa_ptr->shutting_down () != CORBA_B_TRUE) { + if (idle == -1) + oa_ptr->get_request (tcpoa_dispatch, + fwd_ref ? tcpoa_forwarder : 0, + do_threads, &obj_key, 0, env); + else { + timeval tv; + + tv.tv_sec = idle; + tv.tv_usec = 0; + oa_ptr->get_request (tcpoa_dispatch, + fwd_ref ? tcpoa_forwarder : 0, + do_threads, &obj_key, &tv, env); + } + + // + // XXX "env2" should be checked to see if TypeCode::id() reported + // an exception ... + // + CORBA_Environment env2; + + if (env.exception () != 0 + && strcmp ((char *)env.exception ()->id (), + _tc_CORBA_INITIALIZE->id (env2)) == 0) { + print_exception (env.exception (), "TCP_OA::get_request"); + return 1; + } + env.clear (); + } + + // + // Shut down the OA -- recycles all underlying resources (e.g. file + // descriptors, etc). + // + oa_ptr->clean_shutdown (env); + return 0; +} + + +// +// Standard command line parsing utilities used. +// +int +main ( + int argc, + char *const *argv +) +{ + CORBA_Environment env; + CORBA_ORB_ptr orb_ptr; + TCP_OA_ptr oa_ptr; + CORBA_Boolean do_fork = CORBA_B_FALSE; + CORBA_Boolean do_threads = CORBA_B_FALSE; + CORBA_String key = (CORBA_String) "key0"; + char *oa_name = 0; + char *orb_name = "internet"; + int idle = -1; + + // + // Parse the command line, get options + // + int c; + + while ((c = getopt (argc, argv, "di:fk:o:p:t")) != EOF) + switch (c) { + case 'd': // more debug noise + debug_level++; + continue; + + case 'i': // idle seconds b4 exit + idle = atoi (optarg); + continue; + + case 'f': // fork child server + do_fork = CORBA_B_TRUE; + continue; + + case 'k': // key (str) + key = (CORBA_String) optarg; + continue; + + case 'o': // orb name + orb_name = optarg; + continue; + + case 'p': // portnum + oa_name = optarg; + continue; + + case 't': // create thread-per-request + do_threads = CORBA_B_TRUE; + continue; + + // XXX set debug filters ... + + // + // XXX ignore OMG-specified options ... hope nobody ever tries + // to use that "-ORB* param" and "-OA* param" syntax, it flies + // in the face of standard command parsing algorithms which + // require single-character option specifiers. + // + + case '?': + default: + fprintf (stderr, "usage: %s" + " [-d]" + " [-f]" + " [-i idle_seconds]" + " [-k]" + " [-k object_key=key0]" + " [-o orb_name=internet]" + " [-p portnum=5555]" + " [-t]" + "\n", argv [0] + ); + return 1; + } + + orb_ptr = CORBA_ORB_init (argc, argv, orb_name, env); + if (env.exception () != 0) { + print_exception (env.exception (), "ORB init"); + return 1; + } + + oa_ptr = TCP_OA::init (orb_ptr, oa_name, env); + if (env.exception () != 0) { + print_exception (env.exception (), "OA init"); + return 1; + } + + return OA_listen (orb_ptr, oa_ptr, key, idle, do_fork, do_threads); +} + diff --git a/TAO/IIOP/test/test1.cpp b/TAO/IIOP/test/test1.cpp new file mode 100644 index 00000000000..78506e07e24 --- /dev/null +++ b/TAO/IIOP/test/test1.cpp @@ -0,0 +1,383 @@ +// @(#)test1.cpp 1.4 95/09/28 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// TEST stubs for "test1" +// + +#include <stdio.h> + +#include "test1.hh" + + +// +// Define all the stubs ... it's a lot less error prone to do it with +// macros than by hand! +// +// NOTE: the "calldata" is exported for use by the skeletons. At some +// point skeletons will probably be fully abstracted; for now they aren't. +// +// Also, for some reason, name mangling is changed by the explicit +// declaration as "extern" -- if it's not done, linking fails. +// +#define DEFINE_TEST3(typename, truetype, truetypename) \ + static const paramdata test1_ ## typename ## _paramdata [4] = { \ + { _tc_CORBA_ ## truetypename, PARAM_RETURN, 0 }, \ + { _tc_CORBA_ ## truetypename, PARAM_IN, 0 }, \ + { _tc_CORBA_ ## truetypename, PARAM_OUT, 0 }, \ + { _tc_CORBA_ ## truetypename, PARAM_INOUT, 0 }, \ + }; \ + \ + extern const calldata test1_ ## typename ## _calldata; \ + \ + const calldata test1_ ## typename ## _calldata = { \ + "test_" #typename, CORBA_B_TRUE, \ + 4, &test1_ ## typename ## _paramdata [0], \ + 0, 0, \ + }; \ + \ + CORBA_ ## truetype \ + test1_test_ ## typename (test1_ptr target, \ + CORBA_ ## truetype in_a1, \ + CORBA_ ## truetype &out_a2, \ + CORBA_ ## truetype &inout_a3, \ + CORBA_Environment &env) { \ + CORBA_ ## truetype _retval = 0; \ + STUB_Object *_obj; \ + if (target->QueryInterface (IID_STUB_Object, (void **)&_obj) \ + != NOERROR) \ + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); \ + else { \ + _obj->do_call (env, &test1_ ## typename ## _calldata, \ + &_retval, &in_a1, &out_a2, &inout_a3); \ + _obj->Release (); \ + } \ + return _retval; \ + } + +#define DEFINE_TEST(typename, truetype) \ + DEFINE_TEST3(typename, truetype, truetype) + + +// +// Generate a system exception, passing an operation ID that's +// not allowed by IIOP (much less this interface) and verifying +// that the server returns some kind of system exception. +// +static const calldata illegal_calldata = { + "+_illegal", CORBA_B_TRUE, + 0, 0, + 0, 0 +}; + +void +test_illegal (test1_ptr target, CORBA_Environment &env) +{ + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &illegal_calldata + ); + data->Release (); + } +} + + +extern const calldata test1_void_calldata; +const calldata test1_void_calldata = { + "test_void", CORBA_B_TRUE, + 0, 0, + 0, 0 +}; + +void +test1_test_void (test1_ptr target, CORBA_Environment &env) +{ + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &test1_void_calldata + ); + data->Release (); + } +} + +DEFINE_TEST (short, Short); +DEFINE_TEST (long, Long); +DEFINE_TEST (ushort, UShort); +DEFINE_TEST (ulong, ULong); + +#if defined(MIPS) +// +// NOTE: C/C++ compilers as a rule pass a "float" in the space that +// a "double" takes up. Conversions are evidently optional; portability +// forces the following "explicit temporary" hack to work on at least +// one MIPS platform, which converts the parameter to "double" and +// hence changes the binary representation. (Even if that is a compiler +// bug, it's probably required by now for binary compatibility!) +// +// A "-k ansi" compiler flag may be needed to get correct behaviour; +// passing the "in" parameters by reference apparently works too. At +// this time, none of these solutions is used by default. +// +// This stub-level hackery seems like it could be replaced inside of the +// stub interpreter, which could just manually convert "float" parameters +// (all of them) as special cases. But of course, that would slow the +// interpreter down on _every_ call, not just the ones that require it +// (such as this one). Tradeoffs! +// +static const paramdata test1_float_paramdata [4] = { + { _tc_CORBA_Float , PARAM_RETURN , 0 }, + { _tc_CORBA_Float , PARAM_IN , 0 }, + { _tc_CORBA_Float , PARAM_OUT , 0 }, + { _tc_CORBA_Float , PARAM_INOUT , 0 } +}; + +extern const calldata test1_float_calldata; + +const calldata test1_float_calldata = { + "test_float", CORBA_B_TRUE, + 4 , &test1_float_paramdata [0], + 0 , 0 +}; + +CORBA_Float +test1_test_float ( + test1_ptr target, + CORBA_Float in_a1, + CORBA_Float &out_a2, + CORBA_Float &inout_a3, + CORBA_Environment &env +) +{ + CORBA_Float _retval; + + // These three temporaries required due to MIPS compiler bug + CORBA_Float _in_a1 = in_a1; + CORBA_Float _out_a2 = out_a2; + CORBA_Float _inout_a3 = inout_a3; + + target -> data -> do_call (env, + &test1_float_calldata, + &_retval, + &_in_a1, + &_out_a2, + &_inout_a3); + return _retval; +} + +#else +DEFINE_TEST (float, Float); +#endif // MIPS + + +DEFINE_TEST (double, Double); +DEFINE_TEST (boolean, Boolean); +DEFINE_TEST (char, Char); +DEFINE_TEST (octet, Octet); + +/* +CORBA_Any * +test1_test_any (test1_ptr target, + const CORBA_Any &in_a1, + CORBA_Any *&out_a2, + CORBA_Any &inout_a3, + CORBA_Environment &env) +{ + // XXX implement this stub! ... annoying that C++ mapping + // calls for so much special casing +} +*/ + + +DEFINE_TEST3 (TypeCode, TypeCode_ptr, TypeCode); +DEFINE_TEST3 (Principal, Principal_ptr, Principal); +DEFINE_TEST3 (Object, Object_ptr, Object); + +// NOTE: C++ mapping has "in" strings as "const", which doesn't +// show up in this macro ... +DEFINE_TEST3 (string, Char *, String); + +DEFINE_TEST (longlong, LongLong); +DEFINE_TEST (ulonglong, ULongLong); +DEFINE_TEST (wchar, WChar); + +// NOTE: C++ mapping has "in" strings as "const", which doesn't +// show up in this macro ... +DEFINE_TEST3 (wstring, WChar *, WString); + +DEFINE_TEST (longdouble, LongDouble); + +#undef DEFINE_TEST + + +// +// Utility macros used to construct octet codes that are aligned +// on longword boundaries, and with a known byte order. This +// happens to use big endian encoding since it was convenient. +// (Longword alignment is a happy accident of the specification +// of OMG-IDL ... it could have been much worse!) +// +// It'd be much simpler to lay out such data in assembler! +// + +#if defined (WORDS_BIGENDIAN) +# define MAKE_BIG_LONG(a,b,c,d) \ + ((((a) & 0xff) << 24) | (((b) & 0xff) << 16) \ + | (((c) & 0xff) << 8) | ((d) & 0xff)) +# define BIG_ENDIAN_LONG(x) (x) + +#else // LITTLE_ENDIAN +# define MAKE_BIG_LONG(a,b,c,d) \ + ((((d) & 0xff) << 24) | (((c) & 0xff) << 16) \ + | (((b) & 0xff) << 8) | ((a) & 0xff)) +# define BYTE_FROM(n,integer) (((integer)>>(8*(n)))&0xff) +# define BIG_ENDIAN_LONG(integer) \ + MAKE_BIG_LONG (BYTE_FROM(3,integer), BYTE_FROM(2,integer),\ + BYTE_FROM(1,integer), BYTE_FROM(0,integer)) +#endif + + + +// +// "x1" exception typecode ... must be longword aligned +// +static CORBA_Long oc_x1 [] = { + 0, // big endian flag + padding + BIG_ENDIAN_LONG (29), // length of ID string + NUL + MAKE_BIG_LONG ('I', 'D', 'L', ':'), // bytes of ID string + MAKE_BIG_LONG ('E', 'n', 'g', '.'), + MAKE_BIG_LONG ('S', 'U', 'N', '.'), + MAKE_BIG_LONG ('C', 'O', 'M', '/'), + MAKE_BIG_LONG ('t', 'e', 's', 't'), + MAKE_BIG_LONG ('1', '/', 'x', '1'), + MAKE_BIG_LONG (':', '1', '.', '0'), + 0, + BIG_ENDIAN_LONG (1), // (empty) namelen + NUL + 0, + BIG_ENDIAN_LONG (1), // only one struct member + BIG_ENDIAN_LONG (1), // (empty) member name + NUL + 0, + BIG_ENDIAN_LONG (tk_long) +}; +static CORBA_TypeCode tc_x1 (tk_except, sizeof oc_x1, + (unsigned char *)&oc_x1, CORBA_B_FALSE); +CORBA_TypeCode_ptr _tc_test1_x1 = &tc_x1; + + + +// +// "x2" exception typecode ... must be longword aligned +// +static CORBA_Long oc_x2 [] = { + 0, // big endian flag + padding + BIG_ENDIAN_LONG (29), // length of ID string + NUL + MAKE_BIG_LONG ('I', 'D', 'L', ':'), // bytes of ID string + MAKE_BIG_LONG ('E', 'n', 'g', '.'), + MAKE_BIG_LONG ('S', 'U', 'N', '.'), + MAKE_BIG_LONG ('C', 'O', 'M', '/'), + MAKE_BIG_LONG ('t', 'e', 's', 't'), + MAKE_BIG_LONG ('1', '/', 'x', '2'), + MAKE_BIG_LONG (':', '1', '.', '0'), + 0, + BIG_ENDIAN_LONG (1), // (empty) namelen + NUL + 0, + BIG_ENDIAN_LONG (2), // two struct members + + BIG_ENDIAN_LONG (1), // (empty) member name + NUL + 0, + BIG_ENDIAN_LONG (tk_objref), + BIG_ENDIAN_LONG (29), // type ID + NUL + MAKE_BIG_LONG ('I', 'D', 'L', ':'), // bytes of ID string + MAKE_BIG_LONG ('o', 'm', 'g', '.'), + MAKE_BIG_LONG ('o', 'r', 'g', '/'), + MAKE_BIG_LONG ('C', 'O', 'R', 'B'), + MAKE_BIG_LONG ('A', '/', 'O', 'b'), + MAKE_BIG_LONG ('j', 'e', 'c', 't'), + MAKE_BIG_LONG (':', '1', '.', '0'), + 0, + + BIG_ENDIAN_LONG (1), // (empty) member name + NUL + 0, + BIG_ENDIAN_LONG (tk_long) +}; +static CORBA_TypeCode tc_x2 (tk_except, sizeof oc_x2, + (unsigned char *)&oc_x2, CORBA_B_FALSE); +CORBA_TypeCode_ptr _tc_test1_x2 = &tc_x2; + + +// +// parameter, exception, and call descriptions for "test_throw" +// +static const paramdata test1_test_throw_paramdata [1] = { + { _tc_CORBA_Long, PARAM_IN, 0 } +}; + +static CORBA_TypeCode_ptr test1_test_throw_excepts [2] = { + &tc_x1, &tc_x2 +}; + +extern const calldata test1_test_throw_calldata; + +const calldata test1_test_throw_calldata = { + "test_throw", CORBA_B_TRUE, + 1, &test1_test_throw_paramdata [0], + 2, &test1_test_throw_excepts [0] +}; + +// +// "test_throw" stub +// +void +test1_test_throw ( + test1_ptr target, + CORBA_Long case_num, + CORBA_Environment &env // throw (x1, x2) +) +{ + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &test1_test_throw_calldata, + &case_num); + data->Release (); + } +} + +// +// PLEASE EXIT +// + +static const calldata test1_please_exit_calldata = { + "please_exit", CORBA_B_FALSE, + 0, 0, + 0, 0 +}; + +void +test1_please_exit ( + test1_ptr target, + CORBA_Environment &env +) +{ + STUB_Object *data; + + if (target->QueryInterface (IID_STUB_Object, (void **)&data) + != NOERROR) + env.exception (new CORBA_INV_OBJREF (COMPLETED_NO)); + else { + data->do_call (env, &test1_please_exit_calldata + ); + data->Release (); + } +} + diff --git a/TAO/IIOP/test/test1.hh b/TAO/IIOP/test/test1.hh new file mode 100644 index 00000000000..3d5f2fed7bb --- /dev/null +++ b/TAO/IIOP/test/test1.hh @@ -0,0 +1,124 @@ +// @(#)test1.hh 1.2 95/09/12 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// TEST interface for "test1" +// + +#ifndef _TEST1_HH +#define _TEST1_HH + +#include <corba/orb.hh> +#include <corba/stub.hh> + + +#ifdef _MSC_VER +#pragma pack (push, 1) // VC++, known padding rules +#endif // VC++ + +typedef CORBA_UShort test1_ushort; +typedef CORBA_ULong test1_ulong; + +typedef CORBA_LongLong test1_longlong; +typedef CORBA_ULongLong test1_ulonglong; +typedef CORBA_LongDouble test1_longdouble; + +typedef CORBA_Object test1; +typedef test1 *test1_ptr; + +#define DECL_TEST(typename, truetype) \ + truetype \ + test1_test_ ## typename ( \ + test1_ptr target, \ + truetype in_a1, \ + truetype &out_a2, \ + truetype &inout_a3, \ + CORBA_Environment &env \ + ) + +void test_illegal (test1_ptr target, CORBA_Environment &env); +void test1_test_void (test1_ptr target, CORBA_Environment &env); + +DECL_TEST (short, CORBA_Short); +DECL_TEST (long, CORBA_Long); +DECL_TEST (ushort, CORBA_UShort); +DECL_TEST (ulong, CORBA_ULong); +DECL_TEST (float, CORBA_Float); +DECL_TEST (double, CORBA_Double); +DECL_TEST (boolean, CORBA_Boolean); +DECL_TEST (char, CORBA_Char); +DECL_TEST (octet, CORBA_Octet); + +CORBA_Any * +test1_test_any ( + test1_ptr target, + const CORBA_Any &in_a1, + CORBA_Any *&out_a2, + CORBA_Any &inout_a3, + CORBA_Environment &env +); + +DECL_TEST (TypeCode, CORBA_TypeCode_ptr); +DECL_TEST (Principal, CORBA_Principal_ptr); +DECL_TEST (Object, CORBA_Object_ptr); + +// NOTE: CORBA C++ mapping says the "in" string is const +DECL_TEST (string, CORBA_String); + +DECL_TEST (longlong, CORBA_LongLong); +DECL_TEST (ulonglong, CORBA_ULongLong); +DECL_TEST (wchar, CORBA_WChar); + +// NOTE: CORBA C++ mapping says the "in" string is const +DECL_TEST (wstring, CORBA_WString); + +DECL_TEST (longdouble, CORBA_LongDouble); + +#undef DECL_TEST + +extern CORBA_TypeCode_ptr _tc_test1_x1; + +class test1_x1 : public CORBA_UserException { + public: + CORBA_Long case_num; + + test1_x1 (CORBA_Long n) + : CORBA_UserException (_tc_test1_x1), case_num (n) + { } +}; + +extern CORBA_TypeCode_ptr _tc_test1_x2; + +class test1_x2 : public CORBA_UserException { + public: + CORBA_Object_ptr obj; + CORBA_Long case_num; + + test1_x2 (CORBA_Object_ptr obj1, + CORBA_Long n) + : CORBA_UserException (_tc_test1_x2), + obj (obj1), case_num (n) { } + + ~test1_x2 () + { CORBA_release (obj); } +}; + +void +test1_test_throw ( + test1_ptr target, + CORBA_Long case_num, + CORBA_Environment &env // throw (x1, x2) +); + +void +test1_please_exit ( + test1_ptr target, + CORBA_Environment &env +); + +#ifdef _MSC_VER +#pragma pack (pop) // VC++, go back to other padding rules +#endif // VC++ + +#endif // _TEST1_HH + diff --git a/TAO/IIOP/test/test1.idl b/TAO/IIOP/test/test1.idl new file mode 100644 index 00000000000..e6cf20cd2de --- /dev/null +++ b/TAO/IIOP/test/test1.idl @@ -0,0 +1,78 @@ +// @(#)test1.idl 1.1 95/09/11 +// Copyright 1994-1995 by Sun Microsystems, Inc. +// +// TEST basic marshaling tests for all IDL primitive types, modes +// +// This test omits constructed types (struct, union, enum, sequence, and +// array types), and only tests very simple user defined exceptions. +// +// Values returned are well defined functions of the input values: +// +// * For numeric types (octet, short, long, longlong, float, double, +// longdouble, and unsigned variants) the value is cubed. +// * For Boolean, it's the negation. +// * For Any, TypeCode, Principal, Object, char and wchar, +// string and wstring, it's the input value. +// +// The "return" and "out" parameter is the function of the "in" parameter; +// the "inout" parameter is the function of its original value. +// +// The "echo" test has all output values be the input values, with no +// changes to the bit patterns originally transmitted. While easier to +// use to identify some kinds of problem, it is not as complete a test. +// + +#define DECL_TEST(type) \ + type test_ ## type ( in type a1, out type a2, inout type a3) + +#pragma prefix "Eng.SUN.COM" // only for Sun-defined interfaces + +interface test1 { + void test_void (); + + typedef unsigned short ushort; + typedef unsigned long ulong; + + typedef long long longlong; + typedef unsigned long long ulonglong; + typedef long double longdouble; + + DECL_TEST (short); + DECL_TEST (long); + DECL_TEST (ushort); + DECL_TEST (ulong); + DECL_TEST (float); + DECL_TEST (double); + DECL_TEST (boolean); + DECL_TEST (char); + DECL_TEST (octet); + DECL_TEST (any); + DECL_TEST (TypeCode); + DECL_TEST (Principal); + DECL_TEST (Object); // CORBA::Object + DECL_TEST (string); // unbounded string + + DECL_TEST (longlong); + DECL_TEST (ulonglong); + DECL_TEST (wchar); + DECL_TEST (wstring); // unbounded wstring + DECL_TEST (longdouble); + + // + // All cases, "case_num" in the exception is the same as the 'in' param + // * negative or zero, throws x1 + // * positive even cases, throws x2 with obj = null objref + // * positive odd cases, throws x2 with obj = target objref + // + exception x1 { long case_num; }; + exception x2 { Object obj; long case_num; }; + + void test_throw (in long case_num) raises (x1, x2); + + // + // Aid for test cleanup in case server's not told to quit after + // being idle for some time period + // + oneway void please_exit (); +}; + diff --git a/TAO/IIOP/test/test1_clnt.cpp b/TAO/IIOP/test/test1_clnt.cpp new file mode 100644 index 00000000000..53a0c504580 --- /dev/null +++ b/TAO/IIOP/test/test1_clnt.cpp @@ -0,0 +1,636 @@ +// @(#)test1_clnt.cpp 1.5 95/09/24 +// Copyright 1995 by Sun Microsystems, Inc. +// All Rights Reserved +// +// TEST client driver for "test1" +// + +#ifdef USE_IOSTREAM +#include <iostream.h> +#endif + +#include <stdio.h> +#include <string.h> + +#if unix +# include <unistd.h> + +#else // windows +# include "getopt.h" // e.g. GNU's version + +#endif // unix + +#include "test1.hh" +#include "../lib/runtime/debug.hh" + + +extern char *optarg; // missing on some platforms + +extern void +print_exception (const CORBA_Exception *, const char *, FILE *f=stdout); + + +// +// All tests are specified so that the return value and "out" (second) +// parameters are easily tested functions of the "in" (first) parameter, +// and the "inout" (third) parameter is the same function the its +// initial value. Caller is expected to specify two different "in" +// values. This helps make the marshaled bits vary, and turn up a class +// of potential problems that'd be hidden if parameter order had no +// effect on the test. +// +// PERFORM_TEST calls a test for a given type, reporting in cases where +// the ORB or operation failed. The COMPARE symbol can be redefined +// to achieve different functions -- e.g. cubing numbers, identity, +// negation, etc. It should return true iff the correct result was +// returned. +// +// BAD_COMPARE_VALUES is to produce diagnostics when a test fails, +// showing the actual and expected values of returned parameters. +// This helps diagnose specific porting problems. +// +// The RELEASE symbol may be defined to free memory, eliminating client +// side memory leaks in the test. +// +#define RELEASE(X) // NOP by default +#define PERFORM_TEST(name,type,value1,value2) \ + { \ + CORBA_ ## type v1, v2, v3; \ + \ + v1 = (CORBA_ ## type)(value1); \ + v2 = 0; \ + v3 = (CORBA_ ## type)(value2); \ + \ + test_count++; \ + v1 = test1_test_ ## name (target, v1, v2, v3, env); \ + if (env.exception () != 0) { \ + print_exception (env.exception (), "perform test_" #name); \ + error_count++; \ + } else if (!COMPARE (CORBA_ ## type, v1, value1) \ + || !COMPARE (CORBA_ ## type, v2, value1) \ + || !COMPARE (CORBA_ ## type, v3, value2) ) { \ + fprintf (stderr, "bad comparison, test_" #name "\n"); \ + BAD_COMPARE_VALUES(type,v1,v2,v3,value1,value2) \ + error_count++; \ + } \ + RELEASE (v1); RELEASE (v2); RELEASE (v3); \ + } + +// +// This messing about is because the ostream op << cannot always +// be used with all kinds of data ... e.g. LongDouble, TypeCode. +// +#ifdef USE_IOSTREAM +# define DO_IO(x) x +#else +# define DO_IO(x) +#endif +#define BAD_COMPARE_VALUE_OUT(type,v1,v2,v3,value1,value2) \ + DO_IO( cerr << " v1=" << v1; \ + cerr << " expecting " << EXPVAL(CORBA_ ## type, value1) << "\n"; \ + cerr << " v2=" << v2; \ + cerr << " expecting " << EXPVAL(CORBA_ ## type, value1) << "\n"; \ + cerr << " v3=" << v3; \ + cerr << " expecting " << EXPVAL(CORBA_ ## type, value2) << "\n"); +#define BAD_COMPARE_VALUES(type,v1,v2,v3,value1,value2) \ + BAD_COMPARE_VALUE_OUT(type,v1,v2,v3,value1,value2) + + +// +// test utility -- should be able to just use is_equivalent() directly +// but this implementation uses null pointers for nil, so this must +// check for nulls first. (May be noncompliant with C++ mapping!) +// +static CORBA_Boolean +compare_objrefs ( + CORBA_Object_ptr v1, + CORBA_Object_ptr v2 +) +{ + CORBA_Boolean temp; + CORBA_Environment env; + + if (v1 == v2) + return CORBA_B_TRUE; + + if (CORBA_is_nil (v1)) + return CORBA_is_nil (v2); + + temp = v1->_is_equivalent (v2, env); + if (env.exception () != 0) { + print_exception (env.exception (), "compare objref"); + return CORBA_B_FALSE; + } + return temp; +} + + +// +// Helper routine to help sure math (especially for floating point) +// gets done correctly. +// +template <class Type> +Type cube (Type arg) +{ + Type temp = arg; + + temp = temp * arg; + temp = temp * arg; + return temp; +} + + +static int skip_longdouble = 0; + +// +// This just performs the tests ... +// +void +do_tests ( + test1_ptr target, + unsigned loop_count, + unsigned &test_count, + unsigned &error_count +) +{ + int count; + + for (count = 0; count < loop_count; count++) { + CORBA_Environment env; + CORBA_Environment env2; // XXX + + // + // test_void + // + test_count++; + test1_test_void (target, env); + if (env.exception () != 0) { + print_exception (env.exception (), "perform test_void"); + error_count++; + } + + // + // Numeric tests ... try some variety in computation, no real + // rationale in the choice of initial parameter values except + // not to use the same values all the time. + // +#define EXPVAL(type,original_value) ((type) cube((type)(original_value))) +#define COMPARE(type,retval,original_value) \ + ((retval) == EXPVAL(type, original_value)) + + PERFORM_TEST (octet, Octet, count + 29, count - 22); + + PERFORM_TEST (short, Short, count - 23, count + 19); + PERFORM_TEST (ushort, UShort, count + 23, count - 19); + + PERFORM_TEST (long, Long, count - 17, count + 20); + PERFORM_TEST (ulong, ULong, count + 17, count - 20); + +#if !defined (NONNATIVE_LONGLONG) + // don't try this on platforms that don't support + // math on longlongs ... + PERFORM_TEST (longlong, LongLong, count - 177, count + 3); + PERFORM_TEST (ulonglong, ULongLong, count + 177, count - 3); +#endif // !NONNATIVE_LONGLONG + +#if !defined (i386) + // + // XXX not sure what's wrong with the COMPARE macro with respect to + // floating point on x86, since fprintf shows the values basically + // look correct ... but these float/double tests fail. + // + PERFORM_TEST (float, Float, count - 0.29, count + 3.14159); + PERFORM_TEST (double, Double, count * 1.77, count * 2.71); +#endif // !defined (i386) + + +#ifndef NONNATIVE_LONGDOUBLE +#undef BAD_COMPARE_VALUES +#define BAD_COMPARE_VALUES(type,v1,v2,v3,value1,value2) // NOP + + // + // don't try this between two platforms that don't provide + // arithmetic support for LongDouble values ... + // + if (!skip_longdouble) { + PERFORM_TEST (longdouble, LongDouble, + count - 2.33, count * 3.14159); + } + +# undef BAD_COMPARE_VALUES +# define BAD_COMPARE_VALUES(type,v1,v2,v3,value1,value2) \ + BAD_COMPARE_VALUE_OUT(type,v1,v2,v3,value1,value2) +#endif // !NONNATIVE_LONGDOUBLE + +#undef COMPARE +#undef EXPVAL + + + // + // Boolean -- negation + // +#define EXPVAL(type,original_value) (!(type)(original_value)) +#define COMPARE(type,retval,original_value) \ + (((type)(retval)) == EXPVAL(type,original_value)) + + PERFORM_TEST (boolean, Boolean, + (count & 0x01) != 0, (count & 0x01) == 0); +#undef COMPARE +#undef EXPVAL + + // + // Char, WChar -- identity + // +#define EXPVAL(type,original_value) ((type)(original_value)) +#define COMPARE(type,retval,original_value) \ + (((type)(retval)) == EXPVAL(type,original_value)) + + PERFORM_TEST (char, Char, count + 26, count - 5); + PERFORM_TEST (wchar, WChar, count, count + 25); +#undef COMPARE +#undef EXPVAL + + // + // Object_ptr -- identity (special comparision) + // +#define EXPVAL(type,original_value) ((type)(original_value)) +#define COMPARE(type,retval,original_value) \ + (compare_objrefs (retval, original_value) == CORBA_B_TRUE) +#undef RELEASE +#define RELEASE(obj) \ + { CORBA_release (obj); } + + PERFORM_TEST (Object, Object_ptr, target, CORBA_Object::_nil ()); +#undef COMPARE +#undef EXPVAL + + // + // TypeCode_ptr -- identity (special comparision), and verifies + // that most of the built-in typecode constants exist. + // + // XXX should try this on all standard and system exceptions! + // + // XXX this currently tests only marshaling/unmarshaling of + // "no parameter" TypeCodes ... complex ones (objref, struct, + // union, enum, sequence, array, alias, exception) are _NOT_ + // currently attempted. + // + // XXX for the simple typecodes (string, wstring), the single + // "bound" parameter isn't exercised/verified as it should be. + // +#define EXPVAL(type,original_value) ((type)(original_value)) +#define COMPARE(type,retval,original) \ + (((retval) == (original)) || \ + ((retval)->_kind == (original)->_kind)) + +#undef RELEASE +#define RELEASE(tc) \ + { CORBA_release (tc); } + +#undef BAD_COMPARE_VALUES +#define BAD_COMPARE_VALUES(type,v1,v2,v3,value1,value2) // NOP + + { + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Null, _tc_CORBA_Void); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Short, _tc_CORBA_UShort); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Long, _tc_CORBA_ULong); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_LongLong, _tc_CORBA_ULongLong); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Float, _tc_CORBA_Double); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Boolean, _tc_CORBA_Octet); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Any, _tc_CORBA_TypeCode); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_LongDouble, _tc_CORBA_Principal); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_Char, _tc_CORBA_String); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_WChar, _tc_CORBA_WString); + + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_LongDouble, _tc_CORBA_Octet); + + // + // Try all of the standard exception typecodes. + // + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_UNKNOWN, _tc_CORBA_BAD_PARAM); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_NO_MEMORY, _tc_CORBA_IMP_LIMIT); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_COMM_FAILURE, _tc_CORBA_INV_OBJREF); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_OBJECT_NOT_EXIST, _tc_CORBA_NO_PERMISSION); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_INTERNAL, _tc_CORBA_MARSHAL); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_INITIALIZE, _tc_CORBA_NO_IMPLEMENT); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_BAD_TYPECODE, _tc_CORBA_BAD_OPERATION); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_NO_RESOURCES, _tc_CORBA_NO_RESPONSE); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_PERSIST_STORE, _tc_CORBA_BAD_INV_ORDER); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_TRANSIENT, _tc_CORBA_FREE_MEM); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_INV_IDENT, _tc_CORBA_INV_FLAG); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_INTF_REPOS, _tc_CORBA_BAD_CONTEXT); + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_OBJ_ADAPTER, _tc_CORBA_DATA_CONVERSION); + + // + // All the built-in "user defined" system exceptions. + // + PERFORM_TEST (TypeCode, TypeCode_ptr, + _tc_CORBA_BadKind, _tc_CORBA_Bounds); + } +#undef COMPARE +#undef RELEASE + + // + // string (unbounded)-- identity (special comparision) + // +#define COMPARE(type,retval,original_value) \ + (strcmp ((char *)(retval), (char*)(original_value)) == 0) +#undef RELEASE +#define RELEASE(obj) CORBA_string_free(obj) + static const CORBA_Char str1 [] = "small"; + static const CORBA_Char str2 [] = + "relatively long string, constructed" + " with the aid of C++ implicit string" + " catenation, which simplifies much stuff"; + PERFORM_TEST (string, String, str1, str2); +#undef COMPARE +#undef RELEASE + + + // XXX any, principal, wstring ... output _is_ input + + + // + // Three test cases involve throwing user-defined exceptions. + // + { + CORBA_Exception *xp; + + // + // Case one: with parameter <= zero, must throw + // an "x1" exception whose "case_num" is that parameter + // + test_count++; + + test1_test_throw (target, -5, env); + if (env.exception () != 0) { + xp = env.exception (); + + if (strcmp ((char *)xp->id (), + (char *) _tc_test1_x1->id (env2)) != 0) { + error_count++; + fprintf (stderr, "test_throw case 1, " + "wrong exception thrown (id = '%s')\n", + xp->id ()); + } else { + test1_x1 *xp2 = (test1_x1 *) xp; + + if (xp2->case_num != -5) { + error_count++; + fprintf (stderr, "test_throw case 1, " + "wrong value (case_num = '%ld')\n", + xp2->case_num); + } + + // else, right exception was thrown + } + } else { + error_count++; + fprintf (stderr, "test_throw case 1, " + "no exception thrown !!\n"); + } + + + // + // Case two: with positive odd parameter, must throw + // an "x2" exception whose obj is null, and whose case_num + // is that parameter + // + test_count++; + + test1_test_throw (target, 101, env); + if (env.exception () != 0) { + xp = env.exception (); + + if (strcmp ((char *)xp->id (), + (char *) _tc_test1_x2->id (env2)) != 0) { + error_count++; + fprintf (stderr, "test_throw case 2, " + "wrong exception thrown (id = '%s')\n", + xp->id ()); + } else { + test1_x2 *xp2 = (test1_x2 *) xp; + + if (xp2->case_num != 101) { + error_count++; + fprintf (stderr, "test_throw case 2, " + "wrong value (case_num = '%ld')\n", + xp2->case_num); + } + + if (!CORBA_is_nil (xp2->obj)) { + error_count++; + fprintf (stderr, "test_throw case 2, " + "non-null objref thrown\n"); + } + + // else, right exception was thrown + } + } else { + error_count++; + fprintf (stderr, "test_throw case 2, " + "no exception thrown !!\n"); + } + + +#if 0 +// 18-sept-95: commented this out. Work remains to be done in +// this area: (a) ServerRequest::target operation unimplemented, +// so for the servers of current interest this test is no help; +// (b) probable pointer leak in exceptions holding data. + + // + // Case three: with positive even parameter, must throw + // an "x2" exception whose obj is the target, and whose + // case_num is that parameter + // + test_count++; + + test1_test_throw (target, 42, env); + if (env.exception () != 0) { + xp = env.exception (); + + if (strcmp ((char *)xp->id (), + (char *) _tc_test1_x2->id (env2)) != 0) { + error_count++; + fprintf (stderr, "test_throw case 3, " + "wrong exception thrown (id = '%s')\n", + xp->id ()); + } else { + test1_x2 *xp2 = (test1_x2 *) xp; + + if (xp2->case_num != 42) { + error_count++; + fprintf (stderr, "test_throw case 3, " + "wrong value (case_num = '%ld')\n", + xp2->case_num); + } + + CORBA_Boolean status; + + status = target->_is_equivalent (xp2->obj, env); + + if (env.exception () != 0) { + error_count++; + print_exception (env.exception (), + "test_throw/3 call to is_equivalent"); + } else if (status != CORBA_B_TRUE) { + error_count++; + fprintf (stderr, "test_throw case 3, " + "non-equivalent objref thrown\n"); + } + + // else, right exception was thrown + } + } else { + error_count++; + fprintf (stderr, "test_throw case 3, " + "no exception thrown !!\n"); + } + + env.clear (); +#endif // 0 + + } + + // + // test_illegal -- generate a BAD_OPERATION system exception + // from the remote process + // + test_count++; + test_illegal (target, env); + if (env.exception () == 0 + || strcmp ((char *) env.exception()->id(), + (char *) _tc_CORBA_BAD_OPERATION->id (env2)) != 0) { + fprintf (stderr, "couldn't generate BAD_OPERATION exception\n"); + error_count++; + } + } +} + + +int +main ( + int argc, + char *const *argv +) +{ + CORBA_ORB_ptr orb_ptr; + CORBA_Environment env; + CORBA_Object_ptr objref = CORBA_Object::_nil(); + unsigned loop_count = 1; + unsigned tests = 0, errors = 0; + int exit_later = 0; + + orb_ptr = CORBA_ORB_init (argc, argv, "internet", env); + if (env.exception () != 0) { + dexc (env, "ORB initialisation"); + return 1; + } + + // + // Parse and verify parameters. + // + int c; + + while ((c = getopt (argc, argv, "dln:O:x")) != EOF) + switch (c) { + case 'd': // debug flag + debug_level++; + continue; + + case 'l': // skip "long double" test + skip_longdouble++; + continue; + + case 'n': // loop count + loop_count = (unsigned) atoi (optarg); + continue; + + case 'O': // stringified objref + { + objref = orb_ptr->string_to_object ( + (CORBA_String)optarg, env); + if (env.exception () != 0) { + dexc (env, "string2object"); + return 1; + } + } + continue; + + case 'x': + exit_later++; + continue; + + case '?': + default: + fprintf (stderr, "usage: %s" + " [-d]" + " [-l]" + " [-n loopcount]" + " [-O objref]" + " [-x]" + "\n", argv [0] + ); + return 1; + } + + if (CORBA_is_nil (objref) == CORBA_B_TRUE) { + fprintf (stderr, "%s: must identify non-null target objref\n", + argv [0]); + return 1; + } + + + do_tests (objref, loop_count, tests, errors); + + char *progname = strrchr (argv [0], '/'); + + if (progname != 0) + progname += 1; + else + progname = argv [0]; + + fprintf (stderr, "%s: %d loops, %d tests (%d errors)\n", + progname, loop_count, tests, errors); + + if (exit_later) { + test1_please_exit (objref, env); + if (env.exception () != 0) + print_exception (env.exception (), "test1_please_exit"); + } + + CORBA_release (objref); + + return errors != 0; +} diff --git a/TAO/IIOP/test/test1_svr.cpp b/TAO/IIOP/test/test1_svr.cpp new file mode 100644 index 00000000000..668fbae2afc --- /dev/null +++ b/TAO/IIOP/test/test1_svr.cpp @@ -0,0 +1,644 @@ +// @(#)test1_svr.cpp 1.7 95/09/25 +// Copyright 1995 by Sun Microsystems Inc. +// All Rights Reserved +// +// TEST: simple IIOP server for "test1.idl" interface. +// +// Starts up, builds an objref, prints its string, listens for +// messages, responds to them. +// + +#include <stdio.h> +#include <string.h> + +#if unix +# include <unistd.h> // for getopt on some systems + +#else // windows +# include "getopt.h" // e.g. GNU's version + +#endif + +#include "test1.hh" +#include <corba/toa.hh> + +#include "../lib/runtime/debug.hh" + + + +extern char *optarg; // missing on some platforms + +// +// Skeleton code ... just a macro for a bunch of DSI-based method code, +// in lieu of having an IDL compmiler generate static skeletons. Static +// skeletons would be more efficient; most mallocation could go away. +// +// Use by: defining OPERATION macro, call DEFINE_SKEL3 as needed, then +// undef OPERATION. +// +// NOTE: "v1_copy" below is needed to work around a bug with the +// HP9000 G++ 2.6.3 compiler, with "LongLong". +// +// XXX this could probably be a template ... or could even be merged +// directly into the Dynamic Implementation Routine below. +// +// XXX we must currently use IN_COPY_VALUE since the memory consumed +// by the parameters must be deallocated by the ORB. When we get an +// updated version of DSI which provides "send it now" semantics, +// these should preallocate the values and not use IN_COPY_VALUE. A +// net decrease in malloc overhead can be had that way. (NVList should +// also get a public constructor, and a way to provide the buffer.) +// +#define DEFINE_SKEL3(name,truetype,truetypename) \ + static void \ + _test1_test_ ## name ( \ + CORBA_ServerRequest &req, \ + CORBA_Environment &env \ + ) \ + { \ + CORBA_NVList_ptr nvlist; \ + CORBA_ ## truetype scratch = 0; \ + CORBA_Any temp_value (_tc_CORBA_ ## truetypename, \ + &scratch, CORBA_B_FALSE); \ + \ + req.orb ()->create_list (3, nvlist); \ + (void) nvlist->add_value (0, temp_value, \ + CORBA_IN_COPY_VALUE|CORBA_ARG_IN, env); \ + (void) nvlist->add_value (0, temp_value, \ + CORBA_IN_COPY_VALUE|CORBA_ARG_OUT, env); \ + (void) nvlist->add_value (0, temp_value, \ + CORBA_IN_COPY_VALUE|CORBA_ARG_INOUT, env); \ + \ + req.params (nvlist, env); \ + if (env.exception () != 0) { \ + dexc (env, "test1_test_" # name "skeleton, req.params"); \ + return; \ + } \ + \ + CORBA_ ## truetype *v1, *v2, *retval; \ + \ + v1 = (CORBA_ ## truetype *) nvlist->item (0)->value ()->value (); \ + v2 = new CORBA_ ## truetype; \ + *v2 = (CORBA_ ## truetype) OPERATION (*v1); \ + retval = new CORBA_ ## truetype; \ + *retval = (CORBA_ ## truetype) OPERATION (*v1); \ + \ + CORBA_Any_ptr any_val; \ + \ + any_val = nvlist->item (1)->value (); \ + any_val->replace (any_val->type (), v2, CORBA_B_TRUE, env); \ + if (env.exception () != 0) { \ + dexc (env, "test1_test_" # name "skeleton, val2 replace"); \ + return; \ + } \ + \ + v1 = (CORBA_ ## truetype *) nvlist->item (2)->value ()->value (); \ + CORBA_ ## truetype v1copy = *v1; \ + *v1 = (CORBA_ ## truetype) OPERATION (v1copy); \ + \ + any_val = new CORBA_Any (_tc_CORBA_ ## truetypename, \ + retval, CORBA_B_TRUE); \ + req.result (any_val, env); \ + if (env.exception () != 0) { \ + dexc (env, "test1_test_" # name "skeleton, result"); \ + return; \ + } \ + } \ + extern calldata test1_ ## name ## _calldata; + + +extern const calldata test1_void_calldata; + +static void +_test1_test_void ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + + req.orb ()->create_list (0, nvlist); + req.params (nvlist, env); + + if (env.exception () != 0) + dexc (env, "test_throw, get params"); +} + + +// +// Dynamic Skeleton methods for numeric types ... these all just +// cube their parameters in various permutations +// +template <class Type> +Type cube (Type arg) +{ + Type temp = arg; + + temp = temp * arg; + temp = temp * arg; + return temp; +} + +#define OPERATION(n) cube(n) + +DEFINE_SKEL3 (octet, Octet, Octet) + +DEFINE_SKEL3 (short, Short, Short) +DEFINE_SKEL3 (ushort, UShort, UShort) + +DEFINE_SKEL3 (long, Long, Long) +DEFINE_SKEL3 (ulong, ULong, ULong) + +#if !defined (NONNATIVE_LONGLONG) + // don't try this on platforms that don't support + // math on longlongs ... +DEFINE_SKEL3 (longlong, LongLong, LongLong) +DEFINE_SKEL3 (ulonglong, ULongLong, ULongLong) +#endif // !NONNATIVE_LONGLONG + +DEFINE_SKEL3 (float, Float, Float) +DEFINE_SKEL3 (double, Double, Double) + +#if !defined (NONNATIVE_LONGDOUBLE) + // don't try this on platforms that don't support + // math on long doubles ... +DEFINE_SKEL3 (longdouble, LongDouble, LongDouble) +#endif // !NONNATIVE_LONGDOUBLE + +#undef OPERATION + +// +// Dynamic Skeleton methods for Boolean type ... just negates its +// parameters +// +#define OPERATION(x) (!(x)) +DEFINE_SKEL3 (boolean, Boolean, Boolean) +#undef OPERATION + +// +// For character types, output is same as input, no magic needed. +// +#define OPERATION(x) (x) +DEFINE_SKEL3 (char, Char, Char) +DEFINE_SKEL3 (wchar, WChar, WChar) +#undef OPERATION + +// +// For objref, typecode, output is same as input but duplication +// is needed +// +#define OPERATION(x) ((x) ? (x)->AddRef() : 0, x) +DEFINE_SKEL3 (Object, Object_ptr, Object) +DEFINE_SKEL3 (TypeCode, TypeCode_ptr, TypeCode) +#undef OPERATION + +// +// For string, output is copy of input +// +#define OPERATION(x) (CORBA_string_copy(x)) +DEFINE_SKEL3 (string, String, String) +#undef OPERATION + +// +// For wstring, output is copy of input +// +#define OPERATION(x) (CORBA_wstring_copy(x)) +DEFINE_SKEL3 (wstring, WString, WString) +#undef OPERATION + +// +// XXX IMPLEMENT THE REST OF THE DATA TYPES +// +// any, principal -- out is in +// + + +// +// All cases, "case_num" in the exception is the same as the 'in' param +// * negative or zero, throws x1 +// * positive even cases, throws x2 with obj = null objref +// * positive odd cases, throws x2 with obj = target objref +// +// exception x1 { long case_num; }; +// exception x2 { Object obj; long case_num; }; +// +// void test_throw (in long case_num) raises (x1, x2); +// + +extern const calldata test1_test_throw_calldata; + +static void +_test1_test_throw ( + CORBA_ServerRequest &req, + CORBA_Environment &env +) +{ + CORBA_NVList_ptr nvlist; + CORBA_NamedValue_ptr nv; + CORBA_Any temp_value (_tc_CORBA_Long); + CORBA_Long value; + + req.orb ()->create_list (0, nvlist); + nv = nvlist->add_value (0, temp_value, CORBA_ARG_IN, env); + + req.params (nvlist, env); + if (env.exception () != 0) { + dexc (env, "test_throw, get params"); + return; + } + + value = *(CORBA_Long *)nv->value ()->value (); + if (env.exception () != 0) { + dexc (env, "test_throw, param value"); + return; + } + + CORBA_Any_ptr any; + + if (value <= 0) { + test1_x1 *x; + + x = new test1_x1 (value); + any = new CORBA_Any (_tc_test1_x1, x, CORBA_B_TRUE); + + } else if (value & 0x01) { + test1_x2 *x; + + x = new test1_x2 (CORBA_Object::_nil (), value); + any = new CORBA_Any (_tc_test1_x2, x, CORBA_B_TRUE); + + } else { +#if 0 + test1_x2 *x; + + x = new test1_x2 (req.oa()->target (), value); + any = new CORBA_Any (_tc_test1_x2, x, CORBA_B_TRUE); +#else + // + // XXX right now, we don't have a target() operation on the + // TOA ... needs to be added. Verify the client side memory + // leak of pointers embedded in user exceptions is fixed, too. + // + env.exception (new CORBA_IMP_LIMIT (COMPLETED_NO)); + return; +#endif + } + + req.exception (USER_EXCEPTION, any, env); +} + + +// +// This table is used to associate operation names with the Dynamic +// Skeleton method ... someday it could return static skeletons. +// +#define DECL_SKEL(name) \ + { & test1_ ## name ## _calldata, _test1_test_ ## name } + +static const skel_entry test1_operations [] = { + DECL_SKEL (void), + + DECL_SKEL (octet), + DECL_SKEL (char), + + DECL_SKEL (wchar), + + DECL_SKEL (short), + DECL_SKEL (ushort), + DECL_SKEL (long), + DECL_SKEL (ulong), + +#if !defined (NONNATIVE_LONGLONG) + // don't try this on platforms that don't support + // math on longlongs ... + DECL_SKEL (longlong), + DECL_SKEL (ulonglong), +#endif + + DECL_SKEL (float), + DECL_SKEL (double), + +#if !defined (NONNATIVE_LONGDOUBLE) + // don't try this on platforms that don't support + // math on long doubles ... + DECL_SKEL (longdouble), +#endif // !NONNATIVE_LONGDOUBLE + + DECL_SKEL (boolean), + + DECL_SKEL (Object), + DECL_SKEL (TypeCode), + + DECL_SKEL (string), + DECL_SKEL (wstring), + + { & test1_test_throw_calldata, _test1_test_throw }, + + { 0, 0 } +}; + + +// +// Dispatch to method code ... +// +// Knows how to interpret "context" to get target objref, and where to +// get second level skeletons for that target. Both of those operations +// will generally be abstracted (into library and stub code) so the main +// body of this routine would be invisible to most applications. +// +// However, there are applications that need to do this stuff themselves +// (like bridging between environments, e.g. different ORBs, languages, or +// other object systems). Everything needed to work without using an IDL +// compiler is a public, supported API. +// +static void +level1_skeleton ( + CORBA_OctetSeq &key, + CORBA_ServerRequest &req, + void *context, + CORBA_Environment &env +) +{ + // + // Verify that the target object and "this" object have the + // same key. Normally, this would be used to figure out + // which object was the target, and hence which operations + // vector to dispatch the request. + // + CORBA_OctetSeq *obj_key; + + obj_key = (CORBA_OctetSeq *) context; + if (obj_key->length != key.length + || memcmp (obj_key->buffer, key.buffer, + obj_key->length) != 0) { + env.exception (new CORBA_OBJECT_NOT_EXIST (COMPLETED_NO)); +#ifdef DEBUG + if (debug_level) + dmsg_opaque ("request to nonexistent object, key = ", + key.buffer, key.length); +#endif + return; + } + + // + // Find a "level 2 skeleton" for this operation, then + // call it with the right per-object state. + // + const skel_entry *entry; + CORBA_String opname; + + opname = req.op_name (); + for (entry = &test1_operations [0]; entry->op_descriptor; entry++) { + if (strcmp ((char *)opname, entry->op_descriptor->opname) == 0) { + entry->impl_skeleton (req, env); + return; + } + } + + // + // XXX True top-level skeleton code would also have to understand + // the built-in operations: + // + // * _is_a (for narrowing tests) ... can be derived by searching an + // appropriately structured graph of level2 skeletons. + // + // * _non_existent ... if the level1 skeleton consults a module + // which understands object lifespans, this should be simple. + // + // * _get_interface ... could either (a) fail; (b) return the ref + // from some IFR; or most interestingly (c) return a ref to some + // code in this process that can answer all IFR queries from the + // skeleton data structures. + // + // * _get_implementation ... return some administrative hook to + // the object implementation: + // + // No other legal operations start with a character that's not an + // ASCII alphanumeric, for what it's worth. + // + // The skeleton might want to use data in the object key to find + // the objref's type; if it's integrated with object creation, and + // an per-process implementation repository, this should be easy. + // + + // + // bypass level 2 skeletons for this one ... + // + if (strcmp ((char *) opname, "please_exit") == 0) { + dmsg ("I've been asked to shut down..."); + req.oa ()->please_shutdown (env); + dexc (env, "please_exit, please_shutdown"); + return; + } + + // + // No match. Operation not implemented; say so. + // + dmsg1 ("unknown operation, %s", opname); + env.exception (new CORBA_BAD_OPERATION (COMPLETED_NO)); +} + + +// +// Create and print the objref, listen for calls on it until done. +// +extern void +print_exception (const CORBA_Exception *, const char *, FILE *f=stdout); + +int +OA_listen ( + CORBA_ORB_ptr orb_ptr, + TOA_ptr oa_ptr, + CORBA_String key, + int idle +) +{ + // + // Create the object we'll be implementing. + // + CORBA_OctetSeq obj_key; + CORBA_Object_ptr obj; + CORBA_Environment env; + + obj_key.buffer = (CORBA_Octet *) key; + obj_key.length = obj_key.maximum = strlen (key); + + obj = oa_ptr->create (obj_key, (CORBA_String) "", env); + if (env.exception () != 0) { + print_exception (env.exception (), "TOA::create"); + return 1; + } + + // + // Stringify the objref we'll be implementing, and + // print it to stdout. Someone will take that string + // and give it to some client. + // + CORBA_String str; + + str = orb_ptr->object_to_string (obj, env); + if (env.exception () != 0) { + print_exception (env.exception (), "object2string"); + return 1; + } + puts ((char *)str); + fflush (stdout); + dmsg1 ("listening as object '%s'", str); + + // + // Clean up -- "key" is sufficient to dispatch all requests. + // + CORBA_release (obj); + CORBA_string_free (str); + env.clear (); + + // + // Handle requests for this object until we're killed, or one of + // the methods makes us exit. + // + // NOTE: apart from registering the top level skeleton, the rest + // of this loop is exactly what TOA::run() does. It's here to + // show there's no magic. + // + oa_ptr->register_dir (level1_skeleton, &obj_key, env); + if (env.exception () != 0) { + print_exception (env.exception (), "register_dir"); + return 1; + } + + for (;;) { + if (idle == -1) + oa_ptr->get_request (CORBA_B_FALSE, 0, env); + else { + timeval tv; + + tv.tv_sec = idle; + tv.tv_usec = 0; + oa_ptr->get_request (CORBA_B_FALSE, &tv, env); + } + + CORBA_Exception_ptr xp; + + if ((xp = env.exception ()) != 0) { + CORBA_Environment env2; // XXX + char *id; + + id = env.exception ()->id (); + + // + // We get BAD_INV_ORDER if we call get_request() after + // shutdown was initiated. Simpler to rely on that + // than to arrange any handshaking in this simple app. + // + if (strcmp (id, _tc_CORBA_BAD_INV_ORDER->id (env2)) == 0) { + break; + + // + // Other exceptions are errors. + // + } else { + print_exception (env.exception (), "TOA::get_request"); + return 1; + } + } + env.clear (); + } + + // + // Shut down the OA -- recycles all underlying resources (e.g. file + // descriptors, etc). + // + // XXX shutdown is not quite the same as release, unless we want mem + // leaks to cause some rude failure modes. TOA just hasn't been + // updated yet to have any handshake about this though. + // + oa_ptr->Release (); + return 0; +} + + +// +// Standard command line parsing utilities used. +// +int +main ( + int argc, + char *const *argv +) +{ + CORBA_Environment env; + CORBA_ORB_ptr orb_ptr; + TOA_ptr oa_ptr; + CORBA_String key = (CORBA_String) "elvis"; + char *oa_name = 0; + char *orb_name = "internet"; + int idle = -1; + + // + // Parse the command line, get options + // + int c; + + while ((c = getopt (argc, argv, "di:k:o:p:")) != EOF) + switch (c) { + case 'd': // more debug noise + debug_level++; + continue; + + case 'i': // idle seconds b4 exit + idle = atoi (optarg); + continue; + + case 'k': // key (str) + key = (CORBA_String) optarg; + continue; + + case 'o': // orb name + orb_name = optarg; + continue; + + case 'p': // portnum + oa_name = optarg; + continue; + + // XXX set debug filters ... + + // + // XXX ignore OMG-specified options ... hope nobody ever tries + // to use that "-ORB* param" and "-OA* param" syntax, it flies + // in the face of standard command parsing algorithms which + // require single-character option specifiers. + // + + + case '?': + default: + fprintf (stderr, "usage: %s" + " [-d]" + " [-i idle_seconds]" + " [-k object_key=elvis]" + " [-o orbname=internet]" + " [-p oa_name]" + "\n", argv [0] + ); + return 1; + } + + orb_ptr = CORBA_ORB_init (argc, argv, orb_name, env); + if (env.exception () != 0) { + print_exception (env.exception (), "ORB init"); + return 1; + } + + // + // The TOA may or may not actually be named ... + // + oa_ptr = TOA::get_named_toa (orb_ptr, oa_name, env); + if (env.exception () != 0) { + print_exception (env.exception (), "OA init"); + return 1; + } + + return OA_listen (orb_ptr, oa_ptr, key, idle); +} + |