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
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 ); }
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
Create a named communication pipe from TCL. Enter:
su mknod /dev/mypipe p chmod 0666 /dev/mypipe exit
Exit returns you to TCL.
Compile and catalog the FlashBASIC program.
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
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.
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
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.