Synchronizing two D3 processes

On most conventional D3 implementations, synchronizing two D3 processes is done either through polling for the existence of a file or, more preferably, by using a FlashBASIC lock instruction. On most implementations, however, the FlashBASIC lock instruction itself involves, internally, a polling, thus causing the waiting process to consume CPU resources. By using signals, it is possible to have a process waiting for a signal to occur (a Break, log off or a signal generated by another process, possibly a UNIX process or a D3 process from another D3 virtual machine) without any CPU consumption.

This example illustrates one phantom task waiting for a signal sent by a user to read a message in a pipe where the user has deposited the message. In practice, this communication with the message would be better achieved via UNIX messages, but this is merely an example. Also, this is a one to one communication. Multiple partners in this scheme would require semaphores. The server process makes itself visible to the entire system (D3 and UNIX) by writing a temporary file that contains its process ID, which other processes use to send a signal to it.

The signal handler uses SIGUSR1. The message is in ASCII, containing a header and some data, and displays as:

+---+---+--------//-----------+
| |NL | +
+---+---+--------//-----------+
| | |
| | +-                        Data (code dependent)
| +----------                 New line (discarded)
+--------------               command code
           T                  :   Terminate
           M                  :   Message
           R                  :   file receive
  1. Enter the c signal handler. For example:

    setusr1.c:
    #include <signal.h>
    #include "/usr/lib/pick/include/sigmon.h"
    myusr1(){
       /* Just reset the signal for next time */
       signal( SIGUSR1, myusr1 );
    }
       /* Set the new signal handler Set the signal
       handler to the application signal handler.
       Return the address of the previous signal
       handler so that the basic application can
       remove the time out
       */
    void (*setusr1())(){
    return signal(SIGUSR1, myusr1 );
    }
    
  2. Enter the phantom task code in the item dm,bp, pserver. For example:

    pserver
    001 !
    002 * Example of a file server running as
    003 * a phantom
    004 * Commands are received on a named pipe.
    005 *
    006 include dm,bp,unix.h signal.h
    007 include dm,bp,unix.h mode.h
    008 include dm,bp,unix.h fcntl.h
    009 *
    010 * Defines:
    011 equ NL to char(10) ;* UNIX line terminator
    012 equ AM to char(254)
    013 equ PIPE to "/dev/mypipe"
    014 equ BUFSIZE to 10000 ;* max message size
    015 *
    016 char buffer[BUFSIZE]
    107 *
    018 * Remove temporary file
    019 execute '!rm -f /tmp/pserverid'
    020 *
    021 restart:*
    022 *
    023 * Open the communication pipe in read
    024 * only, no wait on read
    025 * If open error, stop, sending a
    026 * message back to the originating
    027 * process.
    028 fd=%open(PIPE, O$RDONLY+O$NDELAY)
    029 if fd<0 then
    030 stop 1,"Cannot open '":PIPE:"'. Error ":system(0)
    031 end
    032 *
    033 * Do not allow BREAK from now on
    034 break off
    035 *
    036 * Create the temporary file which
    037 * contains our pid, so that
    038 * everybody on the system knows the server ID
    040 mypid=%pgetpid(-1)
    041 execute "!echo ":mypid:" > /tmp/pserverid"
    042 *
    043 * Reprogram the alarm handler,
    044 * keeping the previous system handler
    045 * Note the function pointer returned
    046 * by the function is treated as
    047 * a pointer to a character.
    048 old$usr1 = (char*)%setusr1()
    049 *
    050 * Go try read the pipe. If there is no
    051 * message, the signal we received
    052 * was not an application one. May be
    053 * somebody tried to log us off
    054 * so close the pipe, restore the
    055 * signal and wait a bit. If we come
    056 * come back, was a false alarm. Restart.
    057 loop while 1 do
    058 * Wait for a signal
    059 %pause()
    060 *
    061 * Read the pipe
    062 n=%read(fd, buffer, BUFSIZE )
    063 *
    064 begin case
    065 case n=-1
    066 * IO error
    067 gosub terminate
    068 stop 1,"PSERVER: IO error ":system(0)
    069 case n=0
    070 * Pipe is empty...
    071 * The signals we got is not
    072 * the one we expected. Enable
    073 * break to allow for a
    074 * possible
    075 * logoff, and restart
    076 gosub terminate
    077 sleep 1
    078 goto restart
    079 case 1
    080 * Got something (n is the
    081 * number of byte)
    082 * The 1st byte is a code,
    083 * the second a new line
    084 * (discarded)
    085 code=buffer[1,1]
    086 message=buffer[3,n-2]
    087 * Go handle the message
    088 gosub do$it
    089 end case
    090 repeat
    091 *
    092 do$it:*
    093 begin case
    094 case code='T'
    095 * Terminate
    096 gosub terminate
    097 stop 1,"PSERVER: Terminated"
    098 case code='M'
    099 * Message.
    100 execute "msg * PSERVER: ":message
    101 case code='R'
    102 * Receive a file. The message is
    103 * full filename (NL) item (NL)
    104 * text
    105 convert NL to AM in message
    106 filename=message<1>
    107 item=message<2>
    108 message=delete(message,1)
    109 message=delete(message,1)
    110 open filename then
    111 write message on item
    112 close
    113 end else
    114 execute "msg !0 PSERVER: '":filename:"' is not filename"
    115 end
    116 end case
    117 *
    118 Enter
    119 *
    120 * Clean everything
    121 terminate:*
    122 %close(fd)
    123 %signal( SIGUSR1, (char*)old$usr1 )
    124 execute '!rm -f /tmp/pserverid'
    125 break on
    126 Enter
    127 *
    128 end
    
  3. Create a named communication pipe from TCL. Enter:

    Note: You must be a superuser (su) in order to enter the commands below.
    su
    mknod /dev/mypipe p
    chmod 0666 /dev/mypipe
    exit
    

    Exit returns you to TCL.

  4. Compile and catalog the FlashBASIC program.

  5. Compile the c program, and then add it to the built-in function list. For example:

    addbi setusr1
    cc -c setusr1.c
    ar vru libgmu.a setusr1.o
    
  6. To use the customized monitor with a phantom process, shut down and move the customized monitor to the common directory /usr/bin while in single-user mode. For example:

    shutdown
    make -f /usr/lib/pick/Makefile
    init s
    mv /usr/bin/d3 /usr/bin/ap.save
    mv ./d3 /usr/bin
    init 2
    

    Restart the D3 virtual machine.

  7. Create the phantom task server. Type:

    z pserver
    

    The server now waits for a signal to read the message. Normally, this would be done by another process (D3 UNIX). To test this, the shell script sends messages, small UNIX files, or terminates the server.

    snd:
    # Send a command to pserver through a pipe
    PIPE=/dev/mypipe
    # The communication pipe
    SERVERID=/tmp/pserverid
    # where the server puts its PID
    COMMAND=$1
    # Make sure the D3 server is active
    if [ ! -r $SERVERID ]
    then
       echo D3 server is not active
       exit 1
    fi
    # Get the server PID
    PSERVERID=`cat $SERVERID`
    # Send it a signal 0 to test its existence
    kill -0 $PSERVERID
    if [ $? != 0 ]
    then
       echo D3 server is not active. PID is not valid.
       exit 1
    fi
    # Parse command
    case $COMMAND in
       "r"|"R")
          # Receive a file : snd r unix.file
          # D3.file item
          echo "R\n$3\n$4\n~`cat $2~`" > $PIPE
          ;;
       "m"|"M")
          # Send a message: snd m <text>
          shift
          echo "M\n$*" > $PIPE
          ;;
       "t"|"T")
          # Terminate
          echo "T\n" > $PIPE
          ;;
       *)
          echo "usage: snd r unix.file D3.file item"
          echo " snd m message"
          echo " snd t"
          ;;
    esac
    # Wake up the server by sending SIGUSR1
    # -30 for AIX; -16 for LINUX
    kill -30 $PSERVERID
    
  8. Test the server.

    • Enter from shell:

      snd m Hi there
      
    • Enter from D3 to send a D3 message to all D3 users:

      snd r /etc/inittab dm,pointer-file, inittab
      
    • Enter from UNIX to send the file /etc/inittab into the D3 item dm,pointer-file, inittab.

      snd t

      This command terminates the server process.