From: bill@waikato.ac.nz
Newsgroups: news.software.anu-news
Subject: Re: bug fix for NNTP_SERVER, was:Availability of version 6.1xxx?
Date: 23 Nov 92 11:19:37 +1300
Distribution: all
Organization: University of Waikato, Hamilton, New Zealand

In article <1992Nov20.112422.620@duphy4.physics.drexel.edu>, lane@duphy4.physics.drexel.edu writes:
> In article <1992Nov18.120009.615@duphy4.physics.drexel.edu>, lane@duphy4.physics.drexel.edu writes:
>> In article <1992Nov17.082128.44868@kuhub.cc.ukans.edu>, sloane@kuhub.cc.ukans.edu (Bob Sloane) writes:
>>>
>>> It is currently available from sao.aarnet.edu.au.  Current problems
>>> include:
>>>
>>> 1. The search command fails when /NEWSGROUPS is not used.
>>> 2. There are problems selection newsgroups when RCORDER is used.
>>>
>>> There are probably several more that I have forgotten about, or
>>> haven't seen yet.
>>
>> I've gotten a lot of OPCOM messages with ACCVIO's from the NNTP server
>> program (Multinet V3.1B, VMS 5.4).   Anybody found a fix?
>>
> 
> Following up on my own request for a fix:
> 
> It looks like you only get the ACCVIO's when you do *not* have the cache
> enabled.  It's a simple bug in the CACHE.C file, subroutine cache_check_lnm:
> 
> **********************excerpt from CACHE.C********************************
> /* Check to see if logical name NEWS_CACHE has been defined */
> int cache_check_lnm()
> {
>   static int check_lnm = -1;
>   int lnm_length;
>   char message [128];
>   char exec_mode = PSL$C_EXEC,
>        lnm_value[256];
>   unsigned int trnlist[4]={(LNM$_STRING << 16) + LNM$C_NAMLENGTH,
>                              (&lnm_value), (&lnm_length), 0};
> 
>   if (check_lnm != -1)
>       return (check_lnm & 1);
>             /*          ^^^  ---- ADD THESE */
>   else {
>       $DESCRIPTOR( tabnam_desc, "LNM$SYSTEM" );
>       $DESCRIPTOR( lognam_desc, CACHE_LOGNAME );
> 
> *********************************END OF EXCERPT************************
> 
> 
> The access violation itself is coming from cache_get_lock when it is called
> from an exit-handler, which would not have been invoked but for the confused
> status returned by cache_check_lnm.
> 
> If there is anyone taking over maintenance of the ANU-NEWs system, please
> take note.
> --
> 
>  Drexel University       \V                     --Chuck Lane
> ----------------->--------*------------<--------lane@duphy4.hepnet
>      (215) 895-1545      / \  Particle Physics  lane@duphy1.bitnet
> FAX: (215) 895-5934        /~~~~~~~~~~~         lane@duphy4.physics.drexel.edu



There have been a few other bug fixes to the caching code I wrote (suggested
by Bob Sloane). We have yet to pull back the latest beta source here so I am
unfamiliar with which version of the caching code is included with it.
Here is the latest version of CACHE.C (it includes the above fix as well) :

/*
**++
**  FACILITY:
**      CACHE.C
**
**  ABSTRACT:
**      Message Id caching for News.
**      Link with MAP.OBJ and HASH.OBJ.
**      (In News, call the routine CACHE_CHECK before HIST_CHECK and
**      ITM_CHECK in NNTP_SERVER.C).
**
**  AUTHOR:
**      Bill Teahan, University of Waikato, Hamilton, New Zealand
**
**  COPYRIGHT:
**      Copyright  1991
**
**  MODIFICATION HISTORY:
**      Created		Mar-1991     WJT
**	Modified	Mar-1992     SLOANE@kuhub.cc.ukans.edu
**	- Fixed a bug in the exit routine, changed the calling sequence to
**	- include a flag indicating whether or not to actually add the article
**	- to the cache, and changed the hit/miss counting code.
**      Modified	Mar-1992     WJT
**	- Map cache, declare exit handler only once first time CACHE_CHECK1
**	- is called
**      Modified	Nov-1992     LANE@duph4.physics.drexel.edu
&&      - Fix return error in cache_check_lnm()
**--
**/

#include stdio
#include descrip
#include lckdef
#include prvdef
#include jpidef
#include lnmdef
#include psldef
#include "cachedefine.h"

/* global variables */

volatile struct {
   int lksb_l_status;
   int lksb_l_lock_id;
   int lksb_l_value;
} cache_lksb;

int cache_debug = 0;

int cache_saddr, cache_faddr; /* start and finish mapped virtual addresses */

/*
 *  nosyslck
 *
 *  Turn off image installed syslck - BUT if user already had syslck,
 *  then leave it on (as there is no change in user functionality)
 */

void nosyslck()
{
  unsigned int authprivs[2], msyslck[2] ;
  int item = JPI$_PROCPRIV;

  msyslck[0] = PRV$M_SYSLCK;
  msyslck[1] = 0;
  cache_signal(lib$getjpi(&item,0,0,&authprivs,0,0));
  if (!(authprivs[0] & PRV$M_SYSLCK)) cache_signal(sys$setprv(0,msyslck,0,0));
}

/*
 *  syslck
 *
 *  Turn syslck on as a temp priv - assumes image was installed with SYSLCK
 */

void syslck()
{
  unsigned int msyslck[2];

  msyslck[0] = PRV$M_SYSLCK;
  msyslck[1] = 0;
  cache_signal(sys$setprv(1,msyslck,0,0));
}

/* Check to see if logical name NEWS_CACHE has been defined */
int cache_check_lnm()
{
  static int check_lnm = -1;
  int lnm_length;
  char message [128];
  char exec_mode = PSL$C_EXEC,
       lnm_value[256];
  unsigned int trnlist[4]={(LNM$_STRING << 16) + LNM$C_NAMLENGTH,
			     (&lnm_value), (&lnm_length), 0};

  if (check_lnm != -1)
      return (check_lnm & 1);
  else {
      $DESCRIPTOR( tabnam_desc, "LNM$SYSTEM" );
      $DESCRIPTOR( lognam_desc, CACHE_LOGNAME );

      check_lnm = sys$trnlnm( 0, &tabnam_desc, &lognam_desc, &exec_mode,
	    &trnlist );
      if (!(check_lnm & 1)) {
	  open_log_file( CACHE_LOG_FILENAME );
	  strcpy( message, "Cannot find logical NEWS_CACHE" );
	  write_log_file( message );
      }

      return ( check_lnm & 1 );
  }
}

/* set up lock for cacheing */
cache_init_lock()
{
	static int cache_lock_init = 0;

	if (!cache_lock_init) {

	    int status;
	    $DESCRIPTOR( lock_desc, CACHE_LOCKNAME );

	    cache_lock_init = 1;
	    syslck();
	    status = sys$enqw( 0, LCK$K_NLMODE, &cache_lksb, LCK$M_SYSTEM,
		&lock_desc, 0, 0, 0, 0, 0, 0 );
	    /*nosyslck();*/
	    if (!(status & 1)) cache_signal(status);
	}
}

/* get exclusive lock for reading/updating cache */
cache_get_lock( cache )
	CACHE *cache;
{
	int status;

 	cache_init_lock();
	status = sys$enqw( 0, LCK$K_EXMODE, &cache_lksb, LCK$M_CONVERT,
	    0, 0, 0, 0, 0, 0, 0 );
	if (cache->cache_lock_check == 1) { /* double check locking */
	    /* something has gone seriously wrong with the locking! */
	    open_log_file( CACHE_LOG_FILENAME );
	    write_log_file( "Lock check variable already set to 1." );
	}
	cache->cache_lock_check = 1;
 	cache_signal( status );
}

/* free up exclusive lock for reading/updating cache */
cache_free_lock( cache )
	CACHE *cache;
{
	int status;

	cache->cache_lock_check = 0;
	cache_init_lock();
	status = sys$enqw( 0, LCK$K_NLMODE, &cache_lksb, LCK$M_CONVERT,
	    0, 0, 0, 0, 0, 0, 0 );
 	cache_signal( status );
}

/* hash the id */
cache_hash(id)
	CACHE_ID id;
{
	    printf("hash value = %X\n", hash_string( strlen( id ), id ));
}

/* write out what is at the current id to the log file */
int cache_debug_cache_id( cache, msg )
	CACHE *cache;
	int msg;
{
	unsigned int cid;

	cid = cache->cache_cid;
	{
	    char message [128];

	    open_log_file( CACHE_LOG_FILENAME );
	    sprintf( message, "DEBUG %d: Hval = %d, Bptr = %d, Fptr = %d", msg,
		cache->cache_ids [cid].cache_hval,
		cache->cache_ids [cid].cache_bptr,
		cache->cache_ids [cid].cache_fptr );
	    write_log_file( message );
	    sprintf( message, "DEBUG 3: Fptr = %d",
		cache->cache_ids [cid].cache_fptr );
	    write_log_file( message );
	}
}

/* delete oldest id to free up a space for insertion */
int cache_free_cache( cache )
	CACHE *cache;
{
	int fptr, bptr, cid;
	unsigned short hval;

	cid = cache->cache_cid;
	fptr = cache->cache_ids [cid].cache_fptr;
	bptr = cache->cache_ids [cid].cache_bptr;
	hval = cache->cache_ids [cid].cache_hval;

	/* delete the link */
	if (bptr == 0) {
	  if (hval == 0) { /* empty list */
	  }
	  else { /* at head of list */
	    /* no need to check for fptr == 0 as cache_ids [0] not used */
	    cache->cache_ids [fptr].cache_bptr = 0;
	    cache->cache_hash_list [hval] = fptr;
	  }
	}
	else if (fptr == 0) { /* at tail of list */
	    cache->cache_ids [bptr].cache_fptr = 0;
	}
	else { /* at middle of list */
	    cache->cache_ids [fptr].cache_bptr = bptr;
	    cache->cache_ids [bptr].cache_fptr = fptr;
	}

	/* move along to next id - it now becomes the "oldest" */
	cache->cache_cid += 1;
	if (cache->cache_cid >= CACHE_IDS_SIZE)
	    cache->cache_cid = 1; /* wrap around */
	return( cid );
}

/* insert the id into the cache */
cache_insert_cache( id, hvalue, hval, cache )
	CACHE_ID id;
	unsigned int hvalue;
	unsigned short hval;
	CACHE *cache;
{
	int ptr, cid;

	/* delete oldest id to free up space for insertion */
	cid = cache_free_cache( cache );

	/* replace existing id */
	strcpy( cache->cache_ids [cid].cache_id, id );
	cache->cache_ids [cid].cache_hval = hval;
	cache->cache_ids [cid].cache_hvalue = hvalue;

	/* insert at front of linked list */
	ptr = cache->cache_hash_list [hval];
	cache->cache_ids [cid].cache_fptr = ptr;
	cache->cache_ids [cid].cache_bptr = 0;
	cache->cache_ids [ptr].cache_bptr = cid;
	/* no need to check for ptr == 0 as cache_ids [0] not used */
	cache->cache_hash_list [hval] = cid;
}

/* map in the contents of the cache */
CACHE *cache_map_cache()
{
	static int cache_map_init = 0;
	static int cache_map_addr = 0;
	int status;

	if (!cache_map_init) {

	    $DESCRIPTOR( cache_desc, CACHE_FILENAME );
	    $DESCRIPTOR( cache_section_desc, CACHE_SECTION_NAME );
    
	    cache_map_init = 1;
	    status = map_file( &cache_desc, &cache_saddr, &cache_faddr,
			       &cache_section_desc );
	    cache_signal( status );
	    /* printf( "start address = %X, end address = %X\n",
			cache_saddr, cache_faddr ); */
	    cache_map_addr = cache_saddr;
	}
	return( cache_map_addr );
}

/* lookup the id in the cache */
cache_lookup_cache( id, cache, update, debug)
	CACHE_ID id;
	CACHE *cache;
	int update, debug;
{
	int ptr;
	int found = 0;
	int paranoid = 0;
	unsigned int hvalue;
	unsigned short hval;

 	hvalue = hash_string( strlen( id ), id );
	hval = (short) ( hvalue % (CACHE_HASH_SIZE-1) + 1 );

	cache_get_lock( cache );
	/* search hash link list for id */
	ptr = cache->cache_hash_list [hval];
	paranoid = 0; /* counter to prevent infinite looping */
	while ((ptr != 0) && (!found) && (paranoid < 100)) {
	    paranoid += 1;
    	    if (hvalue == cache->cache_ids [ptr].cache_hvalue) {
  	  	found = (strcmp (id, cache->cache_ids [ptr].cache_id ) == 0);
	    }
	    if (!found)
		ptr = cache->cache_ids [ptr].cache_fptr;
	  
	}
	if (update) {
	  if (!found) {
	    cache->cache_lost += 1;
	    cache_insert_cache( id, hvalue, hval, cache );
	  }
	}
	else
	  if (found)
	    cache->cache_found += 1;

	cache_free_lock( cache );
	return( found );
}

/* find the id in the cache */
int cache_find_cache( id, cache )
	CACHE_ID id;
	CACHE *cache;
{
	return( cache_lookup_cache( id, cache, 0, cache_debug ));
}

/* lookup and (if necessary) add the id into the cache */
int cache_add_cache( id, cache )
	CACHE_ID id;
	CACHE *cache;
{
	return( cache_lookup_cache( id, cache, 1, cache_debug ));
}

/* debug the cache */
int cache_debug_cache( toggle, debug )
    int toggle, debug;
{
	if (toggle) cache_debug = (!cache_debug);
	if (debug)
	  if (cache_debug)
	    printf( "Cache debug is now on.\n" );
	  else
	    printf( "Cache debug is now off.\n" );
	return( cache_debug );
}

int cache_exit_handler( s, cache )
	int *s;
	CACHE *cache;
{
	int status;
	struct { int saddr, faddr; } inadr;

	cache_get_lock( cache );
	if (cache_debug)
	    printf( "Updating section...\n" );
	inadr.saddr = cache_saddr;
	inadr.faddr = cache_faddr;
	status = sys$updsecw( &inadr, 0, 0, 0, 0, 0, 0, 0 );
	if ((status != 1) && (cache_debug))
	    printf( "...error updating section %d\n", status );
	cache_free_lock( cache );
}

cache_declare_exit_handler( cache )
	CACHE *cache;
{
	static int exit_status_addr;
	static struct {
	  unsigned int exit_l_forw;
 	  unsigned int exit_a_addr;
 	  unsigned int exit_l_argc;
	  unsigned int exit_a_stat;
	  unsigned int exit_l_arg1;
	} exitblk;

	exitblk.exit_l_forw = 0;
 	exitblk.exit_a_addr = cache_exit_handler;
	exitblk.exit_l_argc = 2;
	exitblk.exit_a_stat = &exit_status_addr;
	exitblk.exit_l_arg1 = cache;
	sys$dclexh( &exitblk );
}

/* Map the cache and check the cache for the id, inserting it if necessary.
 * Call CACHE_CHECK not CACHE_CHECK1 (which is used for trapping unforseen
 * errors). */
int cache_check1(id, add)
  char *id; int add;
{
 	static CACHE *cache;
	int lib$sig_to_ret();
	static int cache_check1_init = 0;

	/* Be paranoid - trap all errors */
	lib$establish( lib$sig_to_ret );

	if ( cache_check1_init == 0 ) {
		cache = cache_map_cache();
	  	/* force update section to file on exit */
		cache_declare_exit_handler( cache );
		cache_check1_init = 1;
	}
    
	return( cache_lookup_cache( id, cache, add, 0 ));
}

int cache_check(id, add)
/* Return 1 if id found in cache; 0 otherwise */
  char *id; int add;
{
	int status;
      	char message [128];
      	    
	if (!cache_check_lnm()) return 0;

	status = cache_check1(id, add);
	if ((status != 1) && (status != 0)) {
	    open_log_file( CACHE_LOG_FILENAME );
	    sprintf( message, "Invalid status returned by CACHE_CHECK = %d",
		status );
	    write_log_file( message );
	}
	return((status == 1));
}