The tpcall(3c) function sends a request to a service subroutine and synchronously waits for a reply. Use the following signature to call the
tpcall() function:
int
tpcall(char *svc, char *
idata, long
ilen, char **
odata, long *
olen, long flags)
Table 6‑1 describes the arguments to the
tpcall() function.
|
|
|
|
|
Pointer that contains the address of the data portion of the request. The pointer must reference a typed buffer that was allocated by a prior call to tpalloc(). Note that the type (and optionally the subtype) of idata must match the type (and optionally the subtype) expected by the service routine. If the types do not match, the system sets tperrno to TPEITYPE and the function call fails.
If the request requires no data, set idata to the NULL pointer. This setting means that the parameter can be ignored. If no data is being sent with the request, you do not need to allocate a buffer for idata.
|
|
Length of the request data in the buffer referenced by idata. If the buffer is a self-defining type, that is, an FML, FML32, VIEW, VIEW32, X_COMMON, X_C_TYPE, or STRING buffer, you can set this argument to zero to indicate that the argument should be ignored.
|
|
Address of a pointer to the output buffer that receives the reply. You must allocate the output buffer using the tpalloc() function. If the reply message contains no data, upon successful return from tpcall(), the system sets *olen to zero, and the pointer and the contents of the output buffer remain unchanged.
You can use the same buffer for both the request and reply messages. If you do, you must set *odata to the address of the pointer returned when you allocate the input buffer. It is an error for this parameter to point to NULL.
|
|
|
|
Flag options. You can OR a series of flags together. If you set this value to zero, the communication is conducted in the default manner. For a list of valid flags and the defaults, refer to tpcall(3c) in the Oracle Tuxedo ATMI C Function Reference.
|
tpcall() returns an integer. On failure, the value of this integer is -1 and the value of
tperrno(5) is set to a value that reflects the type of error that occurred. For information on valid error codes, refer to
tpcall(3c) in the
Oracle Tuxedo ATMI C Function Reference.
To access the new size of the buffer, use the address returned in the *olen parameter. To determine whether a reply buffer has changed in size, compare the size of the reply buffer before the call to
tpcall() with the value of
*olen after its return. If
*olen is larger than the original size, the buffer has grown. If not, the buffer size has not changed.
Listing 6‑1 shows how the client program,
audit.c, makes a synchronous call using the same buffer for both the request and reply messages. In this case, using the same buffer is appropriate because the *
audv message buffer has been set up to accommodate both request and reply information. The following actions are taken in this code:
2.
|
The application initializes the bal and ermsg fields to zero and the NULL string, respectively, in preparation for the values to be returned by the service.
|
3.
|
The svc_name and hdr_type variables represent the service name and the balance type requested, respectively. In this example, these variables represent account and teller, respectively.
|
. . .
/* Create buffer and set data pointer */
audv = (struct aud *)tpalloc("VIEW", "aud", sizeof(struct aud));
/* Prepare aud structure */
audv->b_id = q_branchid;
audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
/* Do tpcall */
if (tpcall(svc_name,(char *)audv,sizeof(struct aud),
(char **)&audv,(long *)&audrl,0)== -1){
(void)fprintf (stderr, "%s service failed\n %s: %s\n",
svc_name, svc_name, audv->ermsg);
retc = -1;
}
else
(void)printf ("Branch %ld %s balance is $%.2f\n",
audv->b_id, hdr_type, audv->balance);
. . .
Listing 6‑2 provides a generic example of how an application test for a change in buffer size after a call to
tpcall(). In this example, the input and output buffers must remain equal in size.
char *svc, *idata, *odata;
long ilen, olen, bef_len, aft_len;
. . .
if (idata = tpalloc("STRING", NULL, 0) == NULL)
error
if (odata = tpalloc("STRING", NULL, 0) == NULL)
error
place string value into idata buffer
ilen = olen = strlen(idata)+1;
. . .
bef_len = olen;
if (tpcall(svc, idata, ilen, &odata, &olen, flags) == -1)
error
aft_len = olen;
if (aft_len > bef_len){ /* message buffer has grown */
if (idata = tprealloc(idata, olen) == NULL)
error
}
Listing 6‑3 is based on the
TRANSFER service, which is part of the
XFER server process of
bankapp. (
bankapp is a sample ATMI application delivered with the Oracle Tuxedo system.) The
TRANSFER service assumes the role of a client when it calls the
WITHDRAWAL and
DEPOSIT services. The application sets the communication flag to
TPSIGRSTRT in these service calls to give the transaction a better chance of committing. The
TPSIGRSTRT flag specifies the action to take if there is a signal interrupt. For more information on communication flags, refer to
tpcall(3c) in the
Oracle Tuxedo ATMI C Function Reference.
/* Do a tpcall to withdraw from first account */
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb,
(long *)&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot withdraw from debit account", (FLDLEN)0);
tpfree((char *)reqfb);
}
...
/* Do a tpcall to deposit to second account */
if (tpcall("DEPOSIT", (char *)reqfb, 0, (char **)&reqfb,
(long *)&reqlen, TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0,
"Cannot deposit into credit account", (FLDLEN)0);
tpfree((char *)reqfb);
}
Listing 6‑4 illustrates a communication call that suppresses transaction mode. The call is made to a service that is not affiliated with a resource manager; it would be an error to allow the service to participate in the transaction. The application prints an accounts receivable report,
accrcv, generated from information obtained from a database named
accounts.
The service routine REPORT interprets the specified parameters and sends the byte stream for the completed report as a reply. The client uses
tpcall() to send the byte stream to a service called
PRINTER, which, in turn, sends the byte stream to a printer that is conveniently close to the client. The reply is printed. Finally, the
PRINTER service notifies the client that the hard copy is ready to be picked up.
#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* report buffer */
long r1len, r2len, r3len; /* buffer lengths of send, 1st reply,
and 2nd reply buffers for report */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* allocate space for report */
leave application and exit program
(void)strcpy(rbuf,
"REPORT=accrcv DBNAME=accounts"); /* send parms of report */
r1len = strlen(rbuf)+1; /* length of request */
start transaction
if (tpcall("REPORT", rbuf, r1len, &rbuf,
&r2len, 0) == -1) /* get report print stream */
error routine
if (tpcall("PRINTER", rbuf, r2len, &rbuf,
&r3len, TPNOTRAN) == -1) /* send report to printer */
error routine
(void)printf("Report sent to %s printer\n",
rbuf); /* indicate which printer */
terminate transaction
free buffer
leave application
}
Note:
|
In the preceding example, the term error routine indicates that the following tasks are performed: an error message is printed, the transaction is aborted, allocated buffers are freed, the client leaves the application, and the program is exited.
|
A possible reason for this check is to guard against errors that may occur in the REPORT service subroutine, resulting in the use of a reply buffer of an incorrect type. Another reason is to prevent changes that are not made consistently across all areas of dependency. For example, another programmer may have changed the
REPORT service to standardize all replies in another
VIEW format without modifying the client process to reflect the change.
#include <stdio.h>
#include "atmi.h"
#include "rview1.h"
main(argc, argv)
int argc;
char * argv[];
{
char *rbuf; /* report buffer */
struct rview1 *rrbuf; /* report reply buffer */
long rlen, rrlen; /* buffer lengths of send and reply
buffers for report */
if (tpinit((TPINIT *) tpinfo) == -1)
fprintf(stderr, "%s: failed to join application\n", argv[0]);
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) { /* allocate space for report */
tpterm();
exit(1);
}
/* allocate space for return buffer */
if (rrbuf = (struct rview1 *)tpalloc("VIEW", "rview1", sizeof(struct rview1)) \ == NULL{
tpfree(rbuf);
tpterm();
exit(1);
}
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts FORMAT=rview1");
rlen = strlen(rbuf)+1; /* length of request */
/* get report in rview1 struct */
if (tpcall("REPORT", rbuf, rlen, (char **)&rrbuf, &rrlen, TPNOCHANGE) == -1) {
fprintf(stderr, "accounts receivable report failed in service call\n");
if (tperrno == TPEOTYPE)
fprintf(stderr, "report returned has wrong view type\n");
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
exit(1);
}
(void)printf("Total accounts receivable %6d\n", rrbuf->total);
(void)printf("Largest three outstanding %-20s %6d\n", rrbuf->name1, rrbuf->amt1);
(void)printf("%-20s %6d\n", rrbuf->name2, rrbuf->amt2);
(void)printf("%-20s %6d\n", rrbuf->name3, rrbuf->amt3);
tpfree(rbuf);
tpfree(rrbuf);
tpterm();
}
The tpacall(3c) function sends a request to a service and immediately returns. Use the following signature to call the
tpacall() function:
int
tpacall(char *svc, char *
data, long
len, long
flags)
Table 6‑2 describes the arguments to the
tpacall() function.
|
|
|
|
|
Pointer that contains the address of the data portion of the request. The pointer must reference a typed buffer that was allocated by a prior call to tpalloc(). Note that the type (and optionally the subtype) of idata must match the type (and optionally the subtype) expected by the service routine. If the types do not match, the system sets tperrno to TPEITYPE and the function call fails.
If the request requires no data, set data to the NULL pointer. This setting means that the parameter can be ignored. If no data is being sent with the request, you do not need to allocate a buffer for data.
|
|
Length of the request data in the buffer referenced by data. If the buffer is a self-defining type, that is, an FML, FML32, VIEW, VIEW32, X_COMMON, X_C_TYPE, or STRING buffer, you can set this argument to zero, indicating that the argument should be ignored.
|
|
Flag options. You can list a group of flags by using the logical OR operator. If you set this value to zero, the communication is conducted in the default manner. For a list of valid flags and defaults, refer to tpacall(3c) in the Oracle Tuxedo ATMI C Function Reference.
|
The tpacall() function sends a request message to the service named in the
svc parameter and immediately returns from the call. Upon successful completion of the call, the
tpacall() function returns an integer that serves as a descriptor used to access the correct reply for the relevant request. While
tpacall() is in transaction mode (as described in
“Writing Global Transactions” on page 9‑1) there may not be any outstanding replies when the transaction commits; that is, within a given transaction, for each request for which a reply is expected, a corresponding reply must eventually be received.
If the value TPNOREPLY is assigned to the
flags parameter, the parameter signals to
tpacall() that a reply is not expected. When this flag is set, on success
tpacall() returns a value of
0 as the reply descriptor. If subsequently passed to the
tpgetrply() function, this value becomes invalid. Guidelines for using this flag value correctly when a process is in transaction mode are discussed in
“Writing Global Transactions” on page 9‑1.
On error, tpacall() returns
-1 and sets
tperrno(5) to a value that reflects the nature of the error.
tpacall() returns many of the same error codes as
tpcall(). The differences between the error codes for these functions are based on the fact that one call is synchronous and the other, asynchronous. These errors are discussed at length in
“Managing Errors” on page 11‑1.
#include <stdio.h>
#include "atmi.h"
main()
{
char *rbuf; /* report buffer */
long rlen, rrlen; /* buffer lengths of send, reply buffers for report */
join application
if (rbuf = tpalloc("STRING", NULL, 0) == NULL) /* allocate space for report */
error
(void)strcpy(rbuf, "REPORT=accrcv DBNAME=accounts");/* send parms of report */
rlen = strlen(rbuf)+1; /* length of request */
start transaction
if (tpcall("REPORT", rbuf, rlen, &rbuf, &rrlen, 0)
== -1) /* get report print stream */
error
if (tpacall("PRINTER", rbuf, rrlen, TPNOTRAN|TPNOREPLY)
== -1) /* send report to printer */
error
. . .
commit transaction
free buffer
leave application
}
audv->balance = 0.0;
(void)strcpy(audv->ermsg, "");
for (i=0; i<NSITE; i++) {
/* Prepare aud structure */
audv->b_id = sitelist[i]; /* routing done on this field */
/* Do tpacall */
if ((cd[i]=tpacall(sname, (char *)audv, sizeof(struct aud), 0))
== -1) {
(void)fprintf (stderr,
"%s: %s service request failed for site rep %ld\n",
pgmname, sname, sitelist[i]);
tpfree((char *)audv);
return(-1);
}
}
int
tpgetrply(int *cd, char **
data, long *
len, long
flags)
Table 6‑3 describes the arguments to the
tpgetrply() function.
The tpsprio(3c) function enables you to set the priority of a message request.
The tpsprio() function affects the priority level of only one request: the next request to be sent by
tpcall() or
tpacall(), or to be forwarded by a service subroutine.
int
tpsprio(int prio, long
flags);
Table 6‑4describes the arguments to the
tpsprio() function.
The following sample code is an excerpt from the TRANSFER service. In this example, the
TRANSFER service acts as a client by sending a synchronous request, via
tpcall(), to the
WITHDRAWAL service.
TRANSFER also invokes
tpsprio() to increase the priority of its request message to
WITHDRAWAL, and to prevent the request from being queued for the
WITHDRAWAL service (and later the
DEPOSIT service) after waiting on the
TRANSFER queue.
/* increase the priority of withdraw call */
if (tpsprio(PRIORITY, 0L) == -1)
(void)userlog("Unable to increase priority of withdraw\n");
if (tpcall("WITHDRAWAL", (char *)reqfb,0, (char **)&reqfb, (long *)
\
&reqlen,TPSIGRSTRT) == -1) {
(void)Fchg(transf, STATLIN, 0, "Cannot withdraw from debit account", \
(FLDLEN)0);
tpfree((char *)reqfb);
tpreturn(TPFAIL, 0,transb->data, 0L, 0);
}
The tpgprio(3c) function enables you to get the priority of a message request.
A requester can call the tpgprio() function after invoking the
tpcall() or
tpacall() function to retrieve the priority of the request message. If a requester calls the function but no request is sent, the function fails, returning
-1 and setting
tperrno(5) to
TPENOENT. Upon success,
tpgprio() returns an integer value in the range of 1 to 100 (where the highest priority value is 100).
If a priority has not been explicitly set using the tpsprio() function, the system sets the message priority to that of the service routine that handles the request. Within an application, the priority of the request-handling service is assigned a default value of 50 unless a system administrator overrides this value.
#include <stdio.h>
#include "atmi.h"
main ()
{
int cd1, cd2; /* call descriptors */
int pr1, pr2; /* priorities to two calls */
char *buf1, *buf2; /* buffers */
long buf1len, buf2len; /* buffer lengths */
join application
if (buf1=tpalloc("FML", NULL, 0) == NULL)
error
if (buf2=tpalloc("FML", NULL, 0) == NULL)
error
populate FML buffers with send request
if ((cd1 = tpacall("service1", buf1, 0, 0)) == -1)
error
if ((pr1 = tpgprio()) == -1)
error
if ((cd2 = tpacall("service2", buf2, 0, 0)) == -1)
error
if ((pr2 = tpgprio()) == -1)\
error
if (pr1 >= pr2) { /* base the order of tpgetrplys on priority of calls */
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
}
else {
if (tpgetrply(&cd2, &buf2, &buf2len, 0) == -1)
error
if (tpgetrply(&cd1, &buf1, &buf1len, 0) == -1)
error
}
. . .
}