/*****************************************************************************/ /* SYNCH_WITH_TIMEOUT.C */ /*****************************************************************************/ /* */ /* Module Name: SYNCH_WITH_TIMEOUT */ /* Purpose: Wait for an asynchronous I/O to complete or to timeout */ /* Author: Jess Goodman, Accu-Weather Inc. */ /* */ /* This routine can be used with drivers and I/O functions that don't */ /* allow for timeout values, such as a IO$_WRITEVBLK to a terminal if */ /* you want to timeout when the terminal is ed. */ /* */ /* Arguments to this routine (all are required): */ /* efn - (longword passed by value) the $QIO event flag argument. */ /* iochan - (word passed by value) the $QIO I/O channel argument. */ /* iosb - (quadword passed via reference) the $QIO IOSB argument. */ /* timeout - (F float passed by value) the seconds before the I/O */ /* will time out. If timeout <= 0 no timeout will occur. */ /* */ /* If a timeout occurs SS$_TIMEOUT will be in the first word of IOSB. */ /* This always returns success unless the efn, iochan or IOSB address */ /* are invalid, the timeout value is more than 10,000 days, or if the */ /* process can not set a VMS timer (for example no more TQELM quota). */ /* */ /* Example of use in C (can be called from any language): */ /* long stat; */ /* long efn=1; */ /* short iochan; */ /* short iosb[4]; */ /* float timeout=5.0; */ /* long iofunc=IO$_WRITEVBLK; */ /* char buffer[80]; */ /* int bytes=80; */ /* int synch_with_timeout( long efn, short channel, short *iosb, */ /* float timeout); *//* prototype to inhibit conversion to double */ /* ... */ /* stat = sys$qio( efn, iochan, iofunc, iosb, NULL, 0, *//* not qiow */ /* buffer, bytes, NULL, NULL, NULL, NULL); */ /* if ((stat&1) == 0) return(stat); */ /* synch_with_timeout( efn, iochan, iosb, timeout); */ /* return(iosb[0]); */ /* */ /* Design Notes: */ /* */ /* This routine will work correctly even if it is used with more */ /* than one active asynchronouse I/O, as long as each active I/O */ /* uses its own event flag and IOSB. The address of the IOSB is */ /* used as the REQIDT argument in the $SETIMR and $CANTIM calls. */ /* When any I/O times out all I/Os on that channel are canceled. */ /* */ /* It was not necessary to use ASTs to implement this algorithm. */ /* The IOSB is automatically cleared when the $QIO is posted and */ /* its first word (completion status) is set to a non-zero value */ /* when the I/O completes. So by testing this word at each step */ /* we can avoid all possible race conditions. */ /* */ /* Usage Note: */ /* */ /* The IO$_READVBLK!IO$M_TIMED (read timeout) was implemented by */ /* the terminal driver as the number of integral seconds between */ /* any two input bytes, so the elapsed time from when the QIO is */ /* posted until it either completes or times out could be longer */ /* than the timeout value. Also the minimum reliable timeout is */ /* 2 seconds; a 1 second timeout could occur in as little as .01 */ /* seconds if the QIO was posted immediately before the terminal */ /* driver's timeout routine ran. So applications might use this */ /* routine instead of or in addition to this IO$M_TIMED timeout. */ /* */ /* History: */ /* V1.0 October 1993 - First pass */ /* V1.1 June 1996 - Prototype functions; can now be called by Fortran */ /* */ /*****************************************************************************/ #include #include /* defines symbolic names for VMS status values */ #include /* defines the format of a VMS status longword */ #include /* defines symbolic constants used by time routines */ #include /* defines all VMS system services used below */ /* prototype lib$cvtf_to_internal_time for use below */ extern long lib$cvtf_to_internal_time( long const *operation, float *timeout, unsigned long vmstime[2]); int synch_with_timeout( long efn, short channel, short volatile iosb[4], float timeout) { int register stat; /* VMS status value */ static long const cvtf_operation = LIB$K_DELTA_SECONDS_F; unsigned long vmstime[2]; /* VMS delta time */ if (timeout > (float)0) /* Ignore a timeout of <=0 for ease of use */ { /* Convert from fractional floating point seconds to VMS delta time. */ stat = lib$cvtf_to_internal_time( &cvtf_operation, &timeout, vmstime); if (!(stat&STS$M_SUCCESS)) return(stat); /* timeout equiv 10,000 days*/ stat = sys$setimr( efn, vmstime, NULL, iosb, 0); /* We use same efn */ if (!(stat&STS$M_SUCCESS)) return(stat); /* efn is verified valid */ if (iosb[0] == 0) /* In case I/O completed before we set timer */ sys$waitfr( efn); /* Wait for IO completion or timer to expire */ if (iosb[0] == 0) /* If set by timer then iostat will be clear */ { sys$clref( efn); /* reclear event flag */ stat = sys$cancel( channel); /* Cancel I/O if still in progress */ if (!(stat&STS$M_SUCCESS)) return(stat); if (iosb[0] == 0) /* In case I/O finished before clref */ sys$waitfr( efn); /* As cancel is not always immediate */ if ((iosb[0] == SS$_ABORT) || /* Started but unfinished */ (iosb[0] == SS$_CANCEL)) /* I/O never even started */ iosb[0] = SS$_TIMEOUT; /* Update either status */ } else /* The I/O completed */ sys$cantim( iosb, 0); /* Cancel wakeup call (never errors) */ } else /* No timeout was specified in call */ stat = sys$synch( efn, iosb); /* Wait for I/O in normal VMS manner */ return(stat); } #ifdef TEST /*Define TEST for a program that writes terminal data with 10 sec. timeout. */ #include #include #include #include main() { int status; short iochan; short volatile iosb[4]; char buffer[5000]; float timeout=10.0; auto $DESCRIPTOR(output,"SYS$OUTPUT"); memset(buffer,'z',sizeof(buffer)); status = sys$assign( &output, &iochan, 0, NULL); if (!(status&1)) return(status); status =sys$qio( 1, iochan, IO$_WRITEVBLK, iosb, NULL, 0, &buffer, sizeof(buffer), NULL, NULL, NULL, NULL); if (!(status&1)) return(status); status = synch_with_timeout( 1, iochan, iosb, timeout); if (!(status&1)) return(status); status = iosb[0]; return(status); } #endif