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

To synchronize two D3 processes:

  1. Enter the c signal handler. For example:

  2. 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 );

    }

  3. Enter the phantom task code in the item dm,bp, pserver. For example:

  4. 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

  5. Create a named communication pipe from TCL. Enter:

  6. 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.

  7. Compile and catalog the FlashBASIC program.

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

  9. addbi setusr1

    cc -c setusr1.c

    ar vru libgmu.a setusr1.o

  10. 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:

  11. 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.

  12. Create the phantom task server. Type:

  13. 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

  14. Test the server.