[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference turris::decc

Title:DECC
Notice:General DEC C discussions
Moderator:TLE::D_SMITHNTE
Created:Fri Nov 13 1992
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:2212
Total number of notes:11045

2108.0. "Missing records when writing to shared file" by CSC32::V_HAVER () Wed Feb 26 1997 15:37

Can someone explain why this is happening?

The following code from a customer is run from multiple processes on the same
VAX.  The program writes to a shared RMS sequential variable file.  The various
processes wake up at the same time and write a message to a log file. Some of
the log messages never appear in the file. 

To reproduce the behavior:

Compile and link with DECC.  This was tested with DEC C V5.5-002 on OpenVMS
VAX V7.0.  Use the attached RUN_TEST.COM procedure to spawn a number of copies
of TEST.EXE. The program will create a new log file each minute.

Examine the log files TEST_hhmm.LOG (where hhmm is the current time). Each
instance of the test program should write its process name in the log file.
Many of the log files will have all messages present, but some, will be missing
one or two messages. It seems to be random as to which message is missing.



I added the error handling (not in original test example submitted by the
customer).   I get the error RMS-F-CUR for the process that seems to be having
problems writing to the file.  Don't understand why this occurs on a write.

Thanks,

Vicky H.


/* TEST.C */
#include <stddef.h>		/* Standard type definitions		    */
#include <stdio.h>		/* Standard C I/O header		    */
#include <stdlib.h>		/* General utility definitions		    */
#include <ctype.h>		/* Character type macro definitions	    */
#include <string.h>		/* String function prototypes		    */
#include <time.h>		/* Time prototypes and structure defs	    */
#include <errno.h>		/* RTL error definitions		    */

#include <starlet.h>
#include <lib$routines.h>
#include <jpidef.h>

#define MILLISEC		-10000		/* 1 ms  = 10000 klunks	    */
#define	PROCNAM_LEN	15

typedef unsigned long QUAD[2];
typedef struct {
   unsigned short  length;
   unsigned short  code;
   char	           *bufadr;
   unsigned short  *retlen;
   int   fini;
  } ITEMLST_T;

static QUAD	wakeTime;
static QUAD	rescheduleInterval = { -1, -1 };
static QUAD	oneMinute = { 60000*MILLISEC, -1 };
static QUAD	currTime;
short int	numvec[7];
int status;

static FILE *OpenTraceFile( );
static get_procname( char * );

main()
{
    FILE	*fp;
    char	procname[16];

    get_procname( procname );
    sys$gettim( &currTime );			/* Get time */
    lib$add_times(&currTime, &oneMinute, &currTime);	/* Add 1 min */
    sys$numtim( &numvec, &currTime );			/* Convert */
    numvec[5] = 0;				/* Round seconds down */
    numvec[6] = 0;				/* Delete millisec */
    lib$cvt_vectim( &numvec, &wakeTime );
    rescheduleInterval[0] = 60 * 1000*MILLISEC;

    sys$schdwk( 0, 0, &wakeTime, &rescheduleInterval );
    while (1)
    {
	sys$hiber();
	fp = OpenTraceFile( );
	status = fprintf( fp, "Test %s\n", procname );
        if (status == -1)
        {
          perror("fprintf: ");
          exit(1);
     }
 
	fclose( fp );
    }
}

/*
**  This routine opens a new trace file whenever the date changes.  The trace
**  file name may or may not contain a format directive.  If it does not, the
**  file is closed and re-opened.
*/
static FILE *OpenTraceFile( )
{
    time_t	    currtime;
    tm_t	    vectime;
    char	    timeStr[17];
    char	    tracename[256];
    long	    status;
    FILE	    *fp;

    /*
    **  Convert current time to ascii and compare to date string in
    **  resource structure. If they are different, open a new file.
    **  Probably should use message time from event structure so that if
    **  message is delayed past midnight, it gets put in previous day's
    **  file rather than current day.
    */
    currtime = time(0);
    memmove( &vectime, localtime( &currtime ), sizeof(tm_t) );
    sprintf( timeStr,"%02.2d%02.2d"
	    ,vectime.tm_hour
	    ,vectime.tm_min
	   );
    sprintf( tracename, "TEST_%s.LOG", timeStr );
    fp = fopen( tracename
	       ,"a"
	       ,"fop=cif"
	       ,"rfm=var"
	       ,"rat=cr"
	       ,"ctx=rec"
	       ,"shr=put"
	      );
    if (fp == NULL)
    {
      perror("fopen: ");
      exit(1);
    }
  
    return fp;
}

static get_procname( char *process_name )
{
register status;
unsigned short retlen;
ITEMLST_T jpilst = { PROCNAM_LEN, JPI$_PRCNAM ,
                     NULL, NULL, 0 };

  jpilst.bufadr = process_name;			/* set address in itmlst */
  jpilst.retlen = &retlen;
  status = sys$getjpiw(0,0,0,&jpilst,0,0,0);	/* get process name */
  if(status&1) process_name[retlen] = '\0';	/* zero term process name */

return(status);
}


$! RUN_TEST.COM
$       MAX = 10
$       i = 0
$ LOOP: i = i+1
$       SPAWN/NOWAIT/PROCESS=TEST_'i' run test
$       if i .lt. MAX then goto LOOP
$
$ WAIT 00:05:10                 !Let it run a while
$
$       i = 0
$ LOOP: i = i+1
$       STOP TEST_'i'
$       if i .lt. MAX then goto LOOP
T.RTitleUserPersonal
Name
DateLines
2108.1Please install latest ECO kit and retestTLE::D_SMITHDuane Smith -- DEC C RTLWed Feb 26 1997 16:4422
    My first attempt was using the DEC C RTL which shipped with OpenVMS
    V7.0.  The problem reproduced during the fifth run.
    
    I then defined DECC$SHR to point to the DEC C RTL which is shipping
    with the latest ECO kit.  After reinitiating the test, it has now 
    completed 18 runs without a hitch.  
    
    Based on this observation, I suggest that you retest after installing 
    the latest DEC C Runtime Library ECO kit available for OpenVMS V7.0 for
    VAX.  The latest kit is called VAXACRT05_070.
    
    There have been several problems corrected in that kit, some of which
    were related to I/O.  A quick scan of the release notes did not show
    an exact match.
    
    Instructions for obtaining ECO kits can be found at our web site:
    
       http://hndymn.zko.dec.com:80/~crtl/
    
    
    Thanks,
    Duane Smith
2108.2TLE::D_SMITHDuane Smith -- DEC C RTLThu Feb 27 1997 06:374
    While I couldn't reproduce, another member of my team can.  It still
    never hurts to try the latest ECO kits prior to reporting problems.
    
    Duane
2108.3Use RMS Directly...XDELTA::HOFFMANSteve, OpenVMS EngineeringThu Feb 27 1997 13:42257
   Since this program is laced with OpenVMS calls, I'd look at replacing
   the C file-I/O calls with RMS calls -- I've got to get one of the local
   C RMS I/O libraries onto the next freeware CD-ROM.  While more recent
   versions of the C RTL are dramatically improving, it's still a UNIX
   emulation on top of OpenVMS, with all the idiosyncrasies that result.
   (UNIX doesn't have anything like RMS's multi-process and interlocking
   and record-management capabilities...)


/*
** COPYRIGHT (c) 1992 BY
** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.
** ALL RIGHTS RESERVED.
**
** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
** ONLY  IN  ACCORDANCE  OF  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER
** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
** OTHER PERSON.  NO TITLE TO AND  OWNERSHIP OF THE  SOFTWARE IS  HEREBY
** TRANSFERRED.
**
** THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE
** AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
** CORPORATION.
**
** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS
** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.
*/

/*
**++
**  Facility:
**
**	Examples
**
**  Version: V1.1
**
**  Abstract:
**
**	Example of working with RMS calls from C
**
**  Author:
**	Steve Hoffman (XDELTA::HOFFMAN)
**
**  Creation Date:  1-Jan-1990
**
**  Modification History:
**	    Hoffman	15-Oct-1994	Updates for DEC C
**--
*/

/*
//  RMS_EXAMPLES.C
//
//  Program displays some RMS calls made from the c language.  Types out
//  the calling process's SYS$LOGIN:LOGIN.COM file to SYS$OUTPUT.
//
//  Included is a main and three subroutines.  The subroutines open,
//  read a record, and close the file.  Several hooks, such as the
//  use of the NAM block to obtain the specification of the file that
//  was actually opened, are included but are not currently used.
//
//  To build:
//
//    $ CC [/DECC] [/DEBUG/NOOPTIM] RMS_EXAMPLES
//    $ LINK [/DEBUG] RMS_EXAMPLES
//    $ RUN [/[NO]DEBUG] RMS_EXAMPLES
*/

#include <lib$routines.h>
#include <rms.h>
#include <starlet.h>
#include <string.h>
#include <ssdef.h>
#include <stdio.h>
#include <stsdef.h>

/*
// RMS_MRS is the maximum record size that can be read (and thus
// displayed) by this program.
*/
#define RMS_MRS	255

/*
// The following is the core data structure for the program.
// The various RMS subroutines all communicate via a pointer
// referencing this struct.
*/
struct RmsFileContext
    {
    struct FAB fab;
    struct RAB rab;
    struct NAM nam;
    char rss[NAM$C_MAXRSS];
    short max_rec_siz;
    char *data_buffer;
    };

RmsFileOpen( void **CtxArg, 
char *FileName, char *DefFileName, int flags, int rss )
    {
    int RetStat;
    struct RmsFileContext *Ctx;
    int howbig = sizeof( struct RmsFileContext );

    /*
    // acquire some space for a Context block.
    */
    RetStat = lib$get_vm( &howbig, &Ctx, 0 );

    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    *CtxArg = (void *) Ctx;

    /*
    // Fill in the various fields of the Context block.
    // -- Builds the File Access Block (FAB), the Record Access
    // Block (RAB) and the Name (NAM) Block.  Along with some
    // other miscellaneous housekeeping stuff.
    */
    Ctx->fab = cc$rms_fab;
    Ctx->rab = cc$rms_rab;
    Ctx->nam = cc$rms_nam;

    Ctx->fab.fab$l_nam = &Ctx->nam;
    Ctx->fab.fab$l_fop = FAB$M_NAM;
    Ctx->fab.fab$b_fac = FAB$M_GET;

    Ctx->fab.fab$l_fna = FileName;
    Ctx->fab.fab$b_fns = strlen( FileName );
    Ctx->fab.fab$l_dna = DefFileName;
    Ctx->fab.fab$b_dns = strlen( DefFileName );

    Ctx->rab.rab$l_fab = &Ctx->fab;

    Ctx->nam.nam$b_rss = NAM$C_MAXRSS;
    Ctx->nam.nam$l_rsa = Ctx->rss;

    Ctx->rab.rab$b_rac = RAB$C_SEQ;

    /*
    // Attempt to open the file...
    */
    RetStat = sys$open( &Ctx->fab, 0, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    /*
    // Allocate a buffer large enough for the biggest record.
    */
    RetStat = lib$get_vm( &RMS_MRS, &Ctx->data_buffer, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    /*
    // Attempt to connect the record stream to the file...
    */
    RetStat = sys$connect( &Ctx->rab, 0, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    return RetStat;
    }

RmsFileRead( void **CtxArg, char **BufAdr, int *BufLen )
    {
    int RetStat;
    struct RmsFileContext *Ctx = *CtxArg;

    Ctx->rab.rab$l_ubf = Ctx->data_buffer;
    Ctx->rab.rab$w_usz = RMS_MRS;

    RetStat = sys$get( &Ctx->rab, 0, 0 );

    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	{
        *BufLen = (char) 0;
	*BufAdr = (char) 0;
	return RetStat;
	}

    *BufAdr = Ctx->rab.rab$l_rbf;
    *BufLen = Ctx->rab.rab$w_rsz;

    return RetStat;
    }

RmsFileClose( void **CtxArg )
    {
    int RetStat;
    struct RmsFileContext *Ctx = *CtxArg;

    /*
    // Free up the record buffer...
    */
    RetStat = lib$free_vm( &RMS_MRS, &Ctx->data_buffer, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    /*
    // Be nice and clean up the record stream...
    */
    RetStat = sys$disconnect( &Ctx->rab, 0, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    /*
    // And close the file...
    */
    RetStat = sys$close( &Ctx->fab, 0, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    /*
    // And free up the allocated memory...
    */
    RetStat = lib$free_vm( &sizeof( struct RmsFileContext ), CtxArg, 0 );
    if ( !$VMS_STATUS_SUCCESS( RetStat ) )
	return RetStat;

    return RetStat;

    }
main()
    {
    int RetStat;
    void *Context;
    char *BufAdr;
    int BufLen;

    /*
    // Open the file.  Minimal checking is performed.  Read access only.
    */
    RetStat = RmsFileOpen( &Context, "LOGIN", "SYS$LOGIN:.COM", 0, 0 );

    /*
    // Read the file.  Minimal checking is performed.
    */
    for (;;)
	{
	RetStat = RmsFileRead( &Context, &BufAdr, &BufLen );
	if ( $VMS_STATUS_SUCCESS( RetStat ) )
	    printf("%*.*s\n", BufLen, BufLen, BufAdr );
	else
	    break;
	}

    /*
    // Close up shop.
    */
    RetStat = RmsFileClose( &Context );

    return RetStat;
    }

2108.4- Well, it works better....CSC32::V_HAVERThu Feb 27 1997 20:4012
I installed VAXACRT05_070 and it seemed to help.  But from note 2108.2 it
appears that it isn't the complete solution, and the real recommendation at
this time is to use RMS calls directly.  Hopefully, I am not misunderstanding
the previous responses.

So, I'll suggest that the customer upgrade (since he should do that anyway)
and install the current ECOs.  However, will let him know that using RMS calls
directly is the best option.

Thx,

Vic
2108.5RMS record locking options are missingTAVENG::BORISBoris Gubenko, ISEMon Mar 03 1997 12:0838
  The %RMS-F-CUR error you got on fprintf() was caused by the failure to
  position the file at end-of-file. The nature of append access mode is that
  the file opened for append is positioned at end-of-file before any output.
  To position a file at end-of-file, the DEC C RTL reads the file records in
  a loop until EOF is reached.

  In your test program you're opening shared file for append specifying only
  "shr=put" without any RMS record locking option. What happened is that the
  default RMS record locking was applied and when two or more streams called
  the $GET service to read the same record while positioning the file at
  end-of-file, one of more requests failed with "%RMS-E-RLK, target record
  currently locked by another stream" status. This explains why the problem
  is "random as to which message is missing" and why the problem not always
  is reproducible.

  There are a number of RMS record locking options which may be specified in
  fopen() statement to fix the problem: RAB$V_REA, RAB$V_RLK, RAB$V_RRL etc.
  The problem went away, for example, after adding the "shr=put" and "rop=rea"
  options to fopen() statement.
  
  We decided, that the right thing to do would be to let the caller to choose
  the most suitable record locking option and not to try to add record locking
  options on the open call on behalf the user.

  We consider this as a documentation issue and are planning to add the
  following text to the next release of the DEC C RTL Reference Manual:

	. If you intend to share a file opened for append, you must specify
	  appropriate share and record locking options, allowing other
	  accessors to read the record. This is because the file is positioned
	  at end-of-file through reading records in a loop until end-of-file
	  is reached.

  Thank you for reporting this. We are sorry for any inconveniences this may
  have caused to you and your customer.

  Boris
2108.6- followup questionsCSC32::V_HAVERTue Mar 04 1997 16:3011
re: .5  

Thanks for the informative response.  It certainly makes the CUR error and the
results understandable.  Just a couple of (hopefully simple) follow up questions.

DEC C RTL behavior on positioning to EOF:  Does rop=eof have any affect on this
behavior, or is it basically the same as saying append "a" access?  Assuming
rop=eof has no affect, then using RMS calls directly would be the only way
around the DEC C RTL behavior.  Is that correct?

vicky
2108.7reply to .6TAVENG::BORISBoris Gubenko, ISEWed Mar 05 1997 00:5855
> [...] Just a couple of (hopefully simple) follow up questions.
> 
> DEC C RTL behavior on positioning to EOF:  Does rop=eof have any affect on this
> behavior, or is it basically the same as saying append "a" access?  Assuming
> rop=eof has no affect, then using RMS calls directly would be the only way
> around the DEC C RTL behavior.  Is that correct?
> 

  The problem is not positioning the file to the end of the file for the
  connect operation for shared append access, but, rather, in seeking to EOF
  performed by the DEC C RTL before each output which, in your case, is not
  needed at all.

  While opening the file for append ("a") or append update ("a+") access mode,
  the DEC C RTL sets the RAB$V_EOF bit in the RAB$L_ROP field before calling
  the $CONNECT service, so the file is positioned at EOF before the first 
  output. For your test example this is enough because RMS itself handles the
  logical end of the file for shared write operation.

  From the description of $PUT RMS service:

	Inserting Records into Sequential Files

	RMS also provides for establishing the logical end of the file when
	two or more processes are doing shared write operation. For example,
	assume that processes A and B are sharing a sequential file and each
	process is putting data into the file. Process A puts a record at the
	end of the file and intends to put another record at the new end-of-file
	location. However, before process A can put the record in the file,
	process B gains access to the file and puts a record at the end of the
	file. In order to ensure, that the next record from process A does not
	overwrite the record just inserted by process B, RMS updates process
	A's pointer to the new end-of-file position; that is, the location
	immediately following the location of process B's record.
	
  Unfortunately, the DEC C RTL does not detect the case when seeking at EOF
  is not needed. This seeking, implemented as calling the $GET in a loop, may
  result in the situation when process A is trying to read the record which is
  currently read and locked for read by process B.

  There is no way to prevent the DEC C RTL from performing this (meaningless
  in your case) seeking at EOF and calling the RMS directly is always a good
  idea.

  However, for your test example, the simple cure is to specify RMS record
  locking option in fopen() statement allowing other accessors to read the
  record. I think, that in your case the most appropriate option is RAB$V_REA -
  lock record for read. As stated in .5, the problem goes away if "shr=get,put"
  and "rop=rea" are specified.

  Note, that seeking at EOF is not always meaningless - consider the case when
  rewind() or fseek() is called between two successive fputs() calls for a file
  opened for append.

  Boris
2108.8CSC64::BLAYLOCKIf at first you doubt,doubt again.Wed Mar 05 1997 18:4327
.  Unfortunately, the DEC C RTL does not detect the case when seeking at EOF
.  is not needed. This seeking, implemented as calling the $GET in a loop, may
.  result in the situation when process A is trying to read the record which is
.  currently read and locked for read by process B.
.
.  There is no way to prevent the DEC C RTL from performing this (meaningless
.  in your case) seeking at EOF and calling the RMS directly is always a good
.  idea.

The RMS solution in this case is:

	rab$b_rac = RAB$C_RFA ;
	rab$l_rfa0 = -1 ;
	rab$w_rfa4 = 511 ; /* Must be less than devblocksize */
	status = sys$find( &rab ) ;
	if ( (!(status&1)) && (status != RMS$_EOF)) {
		/* do error processing */
	}

	rab$b_rac = RAB$C_SEQ ;
	status = sys$put( rab ) ;

the $find positions you at EOF and saves a bit of I/O for what would appear
to be the common case usage of fprintf and append mode.

However, the use of sharing options on the open/fopen calls probably
indicates the use of direct RMS calls instead anyway; as has been suggested.