*BSD News Article 34648


Return to BSD News archive

Path: sserve!newshost.anu.edu.au!harbinger.cc.monash.edu.au!msuinfo!agate!howland.reston.ans.net!cs.utexas.edu!uunet!zib-berlin.de!irz401!uriah!not-for-mail
From: j@uriah.sax.de (J Wunsch)
Newsgroups: comp.os.386bsd.bugs
Subject: Re: Bugs in Xboing 1.8
Date: 19 Aug 1994 19:23:48 +0200
Organization: Private U**X site; member IN e.V.
Lines: 530
Message-ID: <332pr4INN88q@bonnie.sax.de>
References: <32g8vl$6i6@ns.mcs.kent.edu>
NNTP-Posting-Host: bonnie.sax.de

borsburn@mcs.kent.edu (Bret Orsburn) writes:

Yet another item from the list of things i liked to have contributed
to FreeBSD 1.1.5, but didn't actually get the time to do it :-(

>1) When compiled with lockf file locking (the default), the program frequently
>hangs with ps indicating the process is waiting on WCHAN lockf. [...]

Yup, exactly every time you try to re-run it. The first run was always
okay. It succesfully locked the score file against itself ;-)

>A general observation: the audio architecture of xboing 1.8 seems pretty
>radical. There ought to be a way to make noises without forking and killing
>for every sound. [...]

Below is also my hack to get it work with FreeBSD's PCaudio driver.
Due to the overall slowness of it, i built my own version of an Xboing
audio driver. Instead of constantly forking and killing, i use a pipe
and start up a ``sound server''. The idea should be re-usable for real
audio devices, too.

>Meanwhile, I recommend xboing 1.8 (with audio enabled) to the FreeBSD
>development team as a kernel torture test: they probably never anticipated
>this rate of forking and killing!

You're wrong:-) John Dyson once posted his test program to the
hackers' list.  If i remember well, it was something like quickly
forking some 5000 process off the parent...

Here's my patch to xboing:

*** highscore.c.dist	Fri Aug 27 04:35:39 1993
--- highscore.c	Sat Mar 26 14:20:26 1994
***************
*** 13,18 ****
--- 13,19 ----
  #include <sys/param.h>
  #include <sys/file.h>
  #include <sys/types.h>
+ #include <sys/stat.h>
  #include <netinet/in.h>
  #include <pwd.h>
  #include <X11/Xlib.h>
***************
*** 62,68 ****
  static void SortHighScores(void);
  static char *GetHomeDir(void);
  static void DeleteScore(int index);
! static int LockUnlock(int cmd);
  #else
  static int LockUnlock();
  static void DeleteScore();
--- 63,69 ----
  static void SortHighScores(void);
  static char *GetHomeDir(void);
  static void DeleteScore(int index);
! static int LockUnlock(int cmd, int fd);
  #else
  static int LockUnlock();
  static void DeleteScore();
***************
*** 738,746 ****
  	/* Lock the file for me only */
  	if (type == GLOBAL)
  #ifdef USE_FLOCK
! 		id = LockUnlock(LOCK_EX);
  #else
! 		id = LockUnlock(F_LOCK);
  #endif
  
  	/* Read in the lastest scores */
--- 739,747 ----
  	/* Lock the file for me only */
  	if (type == GLOBAL)
  #ifdef USE_FLOCK
! 		id = LockUnlock(LOCK_EX, -1);
  #else
! 		id = LockUnlock(F_LOCK, -1);
  #endif
  
  	/* Read in the lastest scores */
***************
*** 768,774 ****
  				else
  				{
  					/* Don't add as score is smaller */
! 					return False;
  				}
  			}
  		}	/* for */
--- 769,775 ----
  				else
  				{
  					/* Don't add as score is smaller */
! 					goto doUnlock;
  				}
  			}
  		}	/* for */
***************
*** 787,795 ****
  				/* Unlock the file now thanks */
  				if (id != -1) 
  #ifdef USE_FLOCK
! 					id = LockUnlock(LOCK_UN);
  #else
! 					id = LockUnlock(F_ULOCK);
  #endif
  
  				/* Yes - it was placed in the highscore */
--- 788,796 ----
  				/* Unlock the file now thanks */
  				if (id != -1) 
  #ifdef USE_FLOCK
! 					id = LockUnlock(LOCK_UN, id);
  #else
! 					id = LockUnlock(F_ULOCK, id);
  #endif
  
  				/* Yes - it was placed in the highscore */
***************
*** 797,808 ****
  			}
  		}
  
  		/* Unlock the file now thanks */
  		if (id != -1) 
  #ifdef USE_FLOCK
! 					id = LockUnlock(LOCK_UN);
  #else
! 					id = LockUnlock(F_ULOCK);
  #endif
  
  		/* Not even a highscore - loser! */
--- 798,810 ----
  			}
  		}
  
+  doUnlock:
  		/* Unlock the file now thanks */
  		if (id != -1) 
  #ifdef USE_FLOCK
! 					id = LockUnlock(LOCK_UN, id);
  #else
! 					id = LockUnlock(F_ULOCK, id);
  #endif
  
  		/* Not even a highscore - loser! */
***************
*** 1086,1095 ****
  }
  
  #if NeedFunctionPrototypes
! static int LockUnlock(int cmd)
  #else
! static int LockUnlock(cmd)
! 	int cmd;
  #endif
  {
  	int inter = -1;
--- 1088,1097 ----
  }
  
  #if NeedFunctionPrototypes
! static int LockUnlock(int cmd, int fd)
  #else
! static int LockUnlock(cmd, fd)
! 	int cmd, fd;
  #endif
  {
  	int inter = -1;
***************
*** 1103,1110 ****
  		strcpy(filename, HIGH_SCORE_FILE);
  
  	/* Open the highscore file for both read & write */
! 	if (cmd == F_LOCK || cmd == LOCK_EX)
  		inter = open(filename, O_RDWR);
  
  	/* Ok - if successful then lock or unlock the file */
  	if (inter != -1) 
--- 1105,1121 ----
  		strcpy(filename, HIGH_SCORE_FILE);
  
  	/* Open the highscore file for both read & write */
! 	if (
! #ifndef USE_FLOCK
! 	    cmd == F_LOCK
! #else
! 	    cmd == LOCK_EX
! #endif
! 	    )
  		inter = open(filename, O_RDWR);
+ 	else
+ 		/* use old fd to unlock */
+ 		inter = fd;
  
  	/* Ok - if successful then lock or unlock the file */
  	if (inter != -1) 
***************
*** 1115,1121 ****
  #endif
  
  	/* Are we unlocking the file */
! 	if (cmd == F_ULOCK || cmd == LOCK_UN)
  	{
  		/* Close the file now thanks */
  		close(inter);
--- 1126,1138 ----
  #endif
  
  	/* Are we unlocking the file */
! 	if (
! #ifndef USE_FLOCK
! 	    cmd == F_ULOCK
! #else
! 	    cmd == LOCK_UN
! #endif
! 	    )
  	{
  		/* Close the file now thanks */
  		close(inter);
*** /dev/null	Mon Aug 15 21:33:00 1994
--- audio/BSDaudio.c	Thu Apr 21 22:03:56 1994
***************
*** 0 ****
--- 1,299 ----
+ #include "include/copyright.h"
+ 
+ /* SUN Audio format - original code from play.c by Sun */
+ /*
+  * modified for Soeren Schmidt's pcaudio driver for (Free)BSD
+  * by Joerg Wunsch <joerg_wunsch@uriah.sax.de>
+  *
+  * Since pcaudio is a very slow device, a "sound daemon" is forked
+  * off. It gets the filenames to play from its input pipe and does
+  * the dirty work then. Poor man's multi-threading:-)
+  */
+ 
+ /*
+  *  Include file dependencies:
+  */
+ 
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <stddef.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include <errno.h>
+ #include <unistd.h>
+ #include <sys/stat.h>
+ #include <sys/fcntl.h>
+ #include <sys/signal.h>
+ 
+ #include <machine/pcaudioio.h>
+ #include <machine/endian.h>
+ 
+ #include "include/error.h"
+ #include "include/audio.h"
+ 
+ /*
+  *  Internal macro definitions:
+  */
+ 
+ #define BUFFER_SIZE		(1024 * SBUF_SIZE)  
+ 
+ /*
+  *  Internal type declarations:
+  */
+ 
+ #if NeedFunctionPrototypes
+ static void sigalarm_handler(int);
+ #else
+ static void sigalarm_handler();
+ #endif
+ 
+ /*
+  *  Internal variable declarations:
+  */
+ 
+ 
+ unsigned char	buf[BUFFER_SIZE];   /* size should depend on sample_rate */
+ char		*Audio_dev = "/dev/pcaudio";
+ int		Audio_fd = -1;	    /* file descriptor for audio device */
+ int		err;
+ struct stat	st;
+ int		cnt;
+ char		errorString[255];
+ static		pid_t daemonpid = (pid_t) -2;
+ static		int piped[2];
+ 
+ #if NeedFunctionPrototypes
+ int SetUpAudioSystem(Display *display)
+ #else
+ int SetUpAudioSystem(display)
+     Display *display;
+ #endif
+ {
+     /* Validate and open the audio device */
+     if (stat(Audio_dev, &st) < 0)
+     {
+ 	/* No audio device so barf */
+ 	sprintf(errorString, "Cannot stat audio device %s.", Audio_dev);
+ 	ErrorMessage(errorString);
+ 	return False;
+     }
+ 
+     /* Check that the audio device is a character device */
+     if (!S_ISCHR(st.st_mode)) 
+     {
+ 	/* Not a character device which is not right */
+ 	sprintf(errorString, "%s is not an audio device.", Audio_dev);
+ 	ErrorMessage(errorString);
+ 	return False;
+     }
+ 
+     /*
+      * We open the device without the O_NDELAY flag so that someone else
+      * who has the device can release it to us.	 If we use the O_NDELAY
+      * flag, the open will fail even if the process that has the device is
+      * willing to release it.  Unfortunately, an ill-behaved process may not
+      * be willing to give up the device.  In this case, the open will hang.
+      * To prevent this, we wrap an alarm around the open.  If the open hangs
+      * and the alarm goes off, the open will return with errno = EINTR.
+      * suggestion by: jra@hrcms.jazz.att.com (Jeffry R. Abramson)
+      */
+     if ((int)signal(SIGALRM, sigalarm_handler) < 0) 
+     {
+ 	ErrorMessage("signal(SIGALRM) failed.");
+ 	return False;
+     }
+ 
+     alarm(2);
+     /* Try to open audio device */
+     Audio_fd = open(Audio_dev, O_WRONLY);
+     alarm(0);
+ 
+     if ((Audio_fd < 0) && (errno == EBUSY || errno == EINTR)) 
+     {
+ 	/* The audio is in use so barf */
+ 	sprintf(errorString, "%s audio device is busy.", Audio_dev);
+ 	ErrorMessage(errorString);
+ 	return False;
+     }
+ 
+     /* Ok so we cannot open it for some reason */
+     if (Audio_fd < 0) 
+     {
+ 	sprintf(errorString, "Cannot open audio device %s.", Audio_dev);
+ 	ErrorMessage(errorString);
+ 	return False;
+     }
+ 
+     if(daemonpid == (pid_t) -2 && pipe(piped) < 0)
+       {
+ 	ErrorMessage("Unable to create sound daemon pipe");
+ 	return False;
+       }
+ 
+     /* if the daemon is not yet running, start it */
+     if(daemonpid == (pid_t) -2)
+       {
+ 	daemonpid = fork();
+ 	if(daemonpid > 0)
+ 	  (void) close(piped[0]);
+       }
+ 
+     if(daemonpid == (pid_t) -1)
+       {
+ 	/* cannot fork barf */
+ 	ErrorMessage("Cannot fork off sound daemon");
+ 	return False;
+       }
+ 
+     if(daemonpid == 0)
+       {
+ 	/* the child: our sound daemon */
+ 	FILE *pipefile;
+ 
+ 	close(piped[1]);
+ 	if((pipefile = fdopen(piped[0], "r")) == NULL)
+ 	  {
+ 	    ErrorMessage("Cannot make pipe buffer");
+ 	    exit(1);
+ 	  }
+ 
+ 	for(;;)
+ 	  {
+ 	    char soundfile[1024];
+ 	    char *cp;
+ 	    int ifd;
+ 	    struct {
+ 	      char magic[4];	/* magic string ".snd" */
+ 	      long hdr_len;	/* header length, in network byte order */
+ 	    } filehdr;
+ 
+ 	    /*
+ 	     * the pipe is our semaphore; this read will block until
+ 	     * the parent gives us a filename to play
+ 	     */
+ 	    if(fgets(soundfile, 1024, pipefile) == NULL)
+ 	      {
+ 		/* our input pipe has been closed thanks */
+ 		close(Audio_fd);
+ 		exit(0);
+ 	      }
+ 
+ 	    if((cp = strchr(soundfile, '\n')) != 0)
+ 	      *cp = 0;
+ 
+ 	    /* Open the sound file for reading */
+ 	    if ((ifd = open(soundfile, O_RDONLY, 0)) < 0) 
+ 	      {
+ 		/* Issue an error about not opening sound file */
+ 		sprintf(errorString, "Unable to open sound file %s.", soundfile);
+ 		WarningMessage(errorString);
+ 		break;		/* back to outer loop */
+ 	      }
+ 
+ 	    /* validate the header */
+ 	    read(ifd, (char *)&filehdr, sizeof(filehdr));
+ 	    if(strncmp(filehdr.magic, ".snd", 4))
+ 	      {
+ 		sprintf(errorString, "%s does not appear to be a soundfile",
+ 			soundfile);
+ 		WarningMessage(errorString);
+ 		break;
+ 	      }
+ 	    (void) lseek(ifd, SEEK_SET, ntohl(filehdr.hdr_len));
+ 
+ 	    /* At this point, we're all ready to copy the data. */
+ 	    while ((cnt = read(ifd, (char *) buf, BUFFER_SIZE)) >= 0) 
+ 	      {
+ 		/* If input EOF, write an eof marker */
+ 		err = write(Audio_fd, (char *)buf, cnt);
+ 		if (err != cnt) 
+ 		  {
+ 		    /* Did we succeed in writing all the sound out */
+ 		    sprintf(errorString, 
+ 			    "Problem while writing to audio device.");
+ 		    WarningMessage(errorString);
+ 		    break;
+ 		  }
+ 	    
+ 		if (cnt == 0) break;
+ 	      }
+ 
+ 	    if (cnt < 0) 
+ 	      {
+ 		/* Some error - while reading - notify user */
+ 		sprintf(errorString, "Problem while reading soundfile %s", soundfile);
+ 		WarningMessage(errorString);
+ 	      }
+ 	    
+ 	    /* Close the sound file */
+ 	    (void) close(ifd);
+ 	  }
+       }
+ 
+     /* Success in opening audio device */
+     return True;
+ }
+ 
+ #if NeedFunctionPrototypes
+ void FreeAudioSystem(void)
+ #else
+ void FreeAudioSystem()
+ #endif
+ {
+     /* Close the audio device thanks */
+     (void) close(Audio_fd);
+     (void) close(piped[1]);	/* this will shut down the daemon */
+     daemonpid = (pid_t) -2;
+ }
+ 
+ #if NeedFunctionPrototypes
+ void SetMaximumVolume(int Volume)
+ #else
+ void SetMaximumVolume(Volume)
+     int Volume;
+ #endif
+ {
+     /* Set the maximum volume for the audio system */
+ }
+ 
+ #if NeedFunctionPrototypes
+ void playSoundFile(char *filename, int volume)
+ #else
+ void playSoundFile(filename, volume)
+     char *filename;
+     int volume;
+ #endif
+ {
+     char soundfile[1024];
+     char *str;
+ 
+     /* Construct the sounds file path and use env var if exists */
+     if ((str = getenv("XBOING_SOUND_DIR")) != NULL)
+       sprintf(soundfile, "%s/%s.au\n", str, filename);
+     else		
+       sprintf(soundfile, "%s/%s.au\n", SOUNDS_DIR, filename);
+ 
+     /* give the daemon its file name to operate on */
+     write(piped[1], soundfile, strlen(soundfile));
+ }
+ 
+ #if NeedFunctionPrototypes
+ void audioDeviceEvents(void)
+ #else
+ void audioDeviceEvents()
+ #endif
+ {
+     /* None to do */
+ }
+ 
+ /* ARGSUSED */
+ #if NeedFunctionPrototypes
+ static void sigalarm_handler(int signo)
+ #else
+ static void sigalarm_handler(signo)
+      int signo;
+ #endif
+ {
+     /* Used to fix problem when opening the audio device */
+     return;
+ }
-- 
cheers, J"org                             work:    joerg_wunsch@tcd-dresden.de
                                          private:   joerg_wunsch@uriah.sax.de
Steinbach's Guideline for Systems Programming:
        Never test for an error condition you don't know how to handle.