Appendix A
SYNTHESIS Coordination Code Listings
This appendix contains the coordination code generated by SYNTHESIS
in order to create the example applications presented in Chapter
6. In order to make the code more readable, automatically generated
variable names have been replaced with meaningful names and comments
have been inserted where deemed appropriate.
A.1 Experiment 1: A Simple File Viewer
Implementation 1: Data Transfer using
DDE
a. Application Entry Point
Sub Main ()
start_exe0 "exe1.exe" ' start C executable
start_exe0 "exe2.exe" ' start VB executable
End Sub
b. C coordination code (packaged in exe1.exe)
void init_C();
void init_DDE();
void main()
{
init_C();
/* Start user interface component */
select_files();
}
/* Initialization function for C executable */
void init_C();
{
/* init_DDE supplied by the coord. process library */
init_DDE("VB", "RETR");
}
/* This function is called from within
* select_files for each user-selected
* code number
*/
void view_selected_files( int code )
{
char str[20];
/* Convert to string */
itoa(str, code);
/* Send str through a DDE call
* with service name = VB
* and topic name = RETR
*
* dde_call supplied by coord. proc. library
*/
dde_call( "VB", "RETR", str );
}
c. Visual Basic coordination code (packaged in exe2.exe)
Sub Main ()
Init_VB
End Sub
' Initialization subroutine for Visual Basic
Sub Init_VB ()
start_exe0 "\applic\msoffice\winword\winword.exe"
init_DB
init_DDE "VB", "RETR"
End Sub
Sub DDE_Execute(s As String)
Dim code As Integer
Dim filename As String
code = CInt(s) ' convert to integer
filename = retrieve_filename( code )
' Send key sequence CTRL-O + filename + newline to window
' "Microsoft Word"
gui_sendkeys1 "Microsoft Word", "^O@1~",
filename
End Sub
Implementation 2: Data transfer using
a shared file and semaphore synchronization
a. Application Entry Point
Sub Main ()
start_exe0 "exe1.exe" ' start C executable
start_exe0 "exe2.exe" ' start VB executable
End Sub
b. C coordination code (packaged in exe1.exe)
#include <stdio.h>
void init_C();
void main()
{
init_C();
/* Start user interface component */
select_files();
}
/* Initialization function for C executable */
void init_C();
{
/* Initialize semaphores to be used
* for implementing lockstep prerequisite
* synchronization with visual basic
* executable
*/
semaphore_init("Sema1", 0);
semaphore_init("Sema2", 1);
}
/* This function is called from within
* select_files for each user-selected
* code number
*/
void view_selected_files( int code )
{
char str[20];
FILE *fd;
/* Convert to string */
itoa(str, code);
/* Send data through shared file "File1"
* use semaphores "Sema1" and "Sema2" to
* implement lockstep prerequisite protocol
*/
semaphore_P("Sema2");
fd = fopen("File1", "w");
fprintf( fd, "%s\n", str );
fclose( fd );
semaphore_V("Sema1");
}
c. Visual Basic coordination code (packaged in exe2.exe)
Global db_opened As Integer
Global viewer_started As Integer
Sub Main ()
Init_VB
retrieve_loop
End Sub
' Initialization subroutine for Visual Basic
Sub Init_VB ()
' Initialize variables used in persistent prerequisite
' coordination
db_opened = False
viewer_started = False
End Sub
Sub retrieve_loop ()
Dim fn As Integer
Dim s As String
Dim code As Integer
Dim filename As String
:l1
' Implement persistent prereq 1 by checking
' if precedent has been called (and calling it if necessary)
' before each execution of the consequent
If db_opened Then GoTo l2
Open_DB
db_opened = True
:l2
semaphore_P "Sema1"
fn = FreeFile
Open "File1" For Input As fn
Input #fn, s
Close #fn
semaphore_V "Sema2"
code = CInt(s) 'convert back to integer
filename = retrieve_filename( code )
' Implement persistent prereq 2 as explained above
If viewer_started Then GoTo l3
start_exe0 "\applic\msoffice\winword\winword.exe"
viewer_started = True
:l3
gui_sendkeys1 "Microsoft Word", "^O@1~",
filename
' Current architecture does not specify termination
' condition. This subroutine loops indefinitely !!
Goto l1
End Sub
A.2 Experiment 2: Key Word In Context
Implementation 1: Filter components
interconnected using pipes
void kwic_main();
main()
{
kwic_main();
}
void kwic_main()
{
/* Write and Read descriptors for the
* three pipes used
*/
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
/* Thread #1 */
/* Returns pipe descriptors in
* pipe1_w, pipe1_r
*/
create_pipe(&pipe1_w, &pipe1_r);
/* Forks a new UNIX thread.
* Sets old_t1 = 1 in old thread.
* Sets new_t1 = 1 in new thread.
*/
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input( pipe1_w );
goto l99;
l1:
/* Thread #2 */
create_pipe(&pipe2_w, &pipe2_r);
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l2;
shift_lines( pipe1_r, pipe2_w );
goto l99;
l2:
/* Thread #3 */
create_pipe(&pipe3_w, &pipe3_r);
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
sort_lines( pipe2_r, pipe3_w );
goto l99;
l3:
/* Thread #4 */
display_output( pipe3_r );
l99:
}
Implementation 2: Filter components
organized in main program-subroutine architecture
#include <sys/file.h>
void kwic_main();
main ()
{
kwic_main();
}
void kwic_main()
{
/* File descriptors */
int fd1;
int fd2;
int fd3;
int fd4;
int fd5;
int fd6;
fd1 = open("File1", O_WRONLY);
gen_input( fd1 );
close( fd1 );
fd2 = open("File1", O_RDONLY);
fd3 = open("File2", O_WRONLY);
shift_lines( fd2, fd3 );
close( fd2 );
close( fd3 );
fd4 = open("File2", O_RDONLY);
fd5 = open("File3", O_WRONLY);
sort_lines( fd4, fd5 );
close( fd4 );
close( fd5 );
fd6 = open("File3", O_RDONLY);
display_output( fd6 );
close( fd6 );
}
Implementation 3: Filter components
organized in implicit invocation architecture
As explained in Section 6.3.2, this implementation introduces
a redundant layer of coordination and is unnecessarily complex
and inefficient. However, it is included here for completeness.
Figure A.1 shows the overall organization of this implementation,
and can help understand the code listing that follows.
void kwic_init();
void kwic_main();
main()
{
kwic_init();
kwic_main();
}
/* Semaphores for flow 1 */
int sema1, sema2;
/* Semaphores for flow 2 */
int sema3, sema4;
/* Semaphores for flow 3 */
int sema5, sema6;
/* Carrier resource for flow 1 */
char carrier1;
/* Carrier resource for flow 2 */
char carrier2;
/* Carrier resource for flow 3 */
char carrier3;
/* Write and Read descriptors for the
* six pipes used
*/
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
int pipe4_w, pipe4_r;
int pipe5_w, pipe5_r;
int pipe6_w, pipe6_r;
/* Global variables used to
* implement shared events
* set to 1 to signal event
*/
int ev1;
int ev2;
int ev3;
int ev4;
int ev5;
int ev6;
void kwic_init()
{
/* Initialize semaphore pairs, as
* required by the lockstep
* prerequisite coordination
* process, see Figure 4-11
*/
semaphore_init(sema1, 0);
semaphore_init(sema2, 1);
semaphore_init(sema3, 0);
semaphore_init(sema4, 1);
semaphore_init(sema5, 0);
semaphore_init(sema6, 1);
}
void kwic_main()
{
/* Local variables where results
* of fork_thread are stored
*/
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
int old_t4, new_t4;
int old_t5, new_t5;
int old_t6, new_t6;
int old_t7, new_t7;
int old_t8, new_t8;
int old_t9, new_t9;
/* Local variables where results
* of check_event are stored
*/
int test1;
int test2;
int test3;
int test4;
int test5;
int test6;
/* Local variables where results
* of reading carrier resources
* are stored
*/
char c1;
char c2;
char c3;
/* Thread #1 */
/* Returns pipe descriptors in
* pipe1_w, pipe1_r
*/
create_pipe(&pipe1_w, &pipe1_r);
/* Forks a new UNIX thread.
* Sets old_t1 = 1 in old thread.
* Sets new_t1 = 1 in new thread.
*/
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input( pipe1_w );
/* Signal end of input */
generate_event(ev1);
goto l99;
l1:
/* Thread #2 */
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l3;
l11:
/* Loop here until thread #1 generates
* event ev1 to signal no more input
*/
/* Read pipe 1 */
read( pipe1_r, &c1, 1);
/* Write to carrier1 */
semaphore_P(sema2);
carrier1 = c1;
semaphore_V(sema1);
test1 = check_event( ev1 );
if (!test1) goto l11;
/* Signal no more input */
generate_event( ev2 );
goto l99;
l2:
/* Thread #3 */
create_pipe(&pipe2_w, &pipe2_r);
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
l21:
/* Loop here until thread #2 generates
* event ev2 to signal no more input
*/
/* Read from carrier 1 */
semaphore_P(sema1);
c2 = carrier1;
semaphore_V(sema2);
/* Write to pipe 2 */
write( pipe2_w, &c2, 1);
test2 = check_event( ev2 );
if (!test2) goto l21;
goto l99;
l3:
/* Thread #4 */
create_pipe(&pipe3_w, &pipe3_r);
fork_thread(&old_t4, &new_t4);
if (new_t4) goto l4;
shift_lines( pipe2_r, pipe3_w );
/* Signal no more shifted lines */
generate_event( ev3 );
goto l99;
l4:
/* Thread #5 */
fork_thread(&old_t5, &new_t5);
if (new_t5) goto l5;
l41:
/* Loop here until thread #4 generates
* event ev3 to signal no more lines
*/
/* Read pipe 3 */
read( pipe3_r, &c3, 1);
/* Write to carrier2 */
semaphore_P(sema4);
carrier2 = c3;
semaphore_V(sema3);
test3 = check_event( ev3 );
if (!test3) goto l41;
/* Signal no more input */
generate_event( ev4 );
goto l99;
l5:
/* Thread #6 */
create_pipe(&pipe4_w, &pipe4_r);
fork_thread(&old_t6, &new_t6);
if (new_t6) goto l6;
l51:
/* Loop here until thread #5 generates
* event ev4 to signal no more lines
*/
/* Read carrier 2 */
semaphore_P(sema3);
c4 = carrier2;
semaphore_V(sema4);
/* Write pipe 4 */
write( pipe4_w, &c4, 1);
test4 = check_event( ev4 );
if (!test4) goto l51;
goto l99;
l6:
/* Thread #7 */
create_pipe(&pipe5_w, &pipe5_r);
fork_thread(&old_t7, &new_t7);
if (new_t7) goto l7;
sort_lines( pipe4_r, pipe5_w );
/* Signal no more sorted lines */
generate_event( ev5 );
goto l99;
l7:
/* Thread #8 */
fork_thread(&old_t8, &new_t8);
if (new_t8) goto l8;
l71:
/* Loop here until thread #7 generates
* event ev5 to signal no more input
*/
/* Read pipe 5 */
read( pipe5_r, &c1, 1);
/* Write to carrier3 */
semaphore_P(sema6);
carrier3 = c5;
semaphore_V(sema5);
test5 = check_event( ev5 );
if (!test5) goto l71;
/* Signal no more input */
generate_event( ev6 );
goto l99;
l8:
/* Thread #9 */
create_pipe(&pipe6_w, &pipe6_r);
fork_thread(&old_t8, &new_t8);
if (new_t8) goto l9;
l81:
/* Loop here until thread #8 generates
* event ev6 to signal no more input
*/
/* Read from carrier 3 */
semaphore_P(sema5);
c6 = carrier3;
semaphore_V(sema6);
/* Write to pipe 6 */
write( pipe6_w, &c6, 1);
test6 = check_event( ev6 );
if (!test6) goto l81;
goto l99;
l9:
/* Thread #10 */
display_output( pipe6_r );
l99:
}
Implementation 4: Server components
interconnected using pipes
a. Automatically generated coordination code
/* Write and Read pipe descriptors */
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
/* Global variables used to signal
* shared events.
* Set to 1 by generate_event
*/
int ev1;
int ev2;
void kwic_main();
main()
{
kwic_main();
}
void kwic_main()
{
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
char line1[256];
char line2[256];
int test1;
int test2;
/* Thread #1 (Generate Text) */
create_pipe(&pipe1_w, &pipe1_r);
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input();
/* Signal end of input to thread #2 */
generate_event(ev1);
goto l99;
l1:
/* Thread #2 (Shift Lines) */
create_pipe(&pipe2_w, &pipe2_r);
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l2;
l11:
/* Loop here until thread #1 generates
* event ev1 to signal end of input
* stream
*/
convert_from_byte_stream( pipe1_r, line1 );
shift_line( line1 );
/* Sets test1 to 1 if ev1 != 0 */
test1 = check_event( ev1 );
if (!test1) goto l11;
/* Signal no more lines to thread #3
*/
generate_event(ev2);
goto l99;
l2:
/* Thread #3 (Sort Lines) */
create_pipe(&pipe3_w, &pipe3_r);
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
l21:
/* Loop here until thread #2 generates
* event ev2 to signal no more lines
*/
convert_from_byte_stream( pipe2_r, line2 );
next_line_to_sort( line2 );
test2 = check_event( ev2 );
if (!test2) goto l21;
/* All lines have been read. Sort
* them! */
do_sort();
goto l99;
l3:
/* Thread #4 (Display Output) */
display_output();
l99:
}
/* Called by gen_input for each new
* input line */
void next_input_line(char *line)
{
convert_to_byte_stream( pipe1_w, line );
}
/* Called by USER-WRITTEN
* convert_to_byte_stream
* for each byte of a line
*/
void write_next_byte_in_stream(int pipe, char byte)
{
write( pipe, byte, 1);
}
/* Called by USER-WRITTEN
* convert_from_byte_stream
* to get next byte in stream
*/
char read_next_byte_in_stream(int pipe)
{
char c;
read( pipe, &c, 1);
return( c );
}
/* Called by shift_line for each
* circular shift of its input line
*/
void next_shifted_line(char *line)
{
convert_to_byte_stream( pipe2_w, line );
}
/* Called by do_sort for each final
* sorted line */
void next_sorted_line( char *line)
{
convert_to_byte_stream( pipe3_w, line );
}
char rline[256];
/* Called by display_output to get
* next line */
char *get_next_output_line()
{
convert_from_byte_stream( pipe3_r );
return( rline );
}
b. User-written coordination code
/* Convert a null-terminated string into
* a newline-terminated stream of bytes
*
* stream is the stream descriptor
* In UNIX it may be either a pipe or an
* open file descriptor
*/
void convert_to_byte_stream(int stream, char *line)
{
char c;
char *p = line;
while( c = *p++ )
write_next_byte_in_stream(stream, c);
write_next_byte_in_stream(stream, '\n');
}
/* Convert a newline-terminated stream
* of bytes
* into a null-terminated string
*
* stream is a stream (pipe or file)
* descriptor
*/
void convert_from_byte_stream(int stream, char *line)
{
char *p = line;
char c;
while( (c =
read_next_byte_in_stream(stream)
!= '\n' )
*p++ = c;
*p = '\0';
}
Implementation 5: Server components
organized in main program-subroutine architecture
#include <sys/file.h>
int fd1;
int fd2;
void kwic_main();
main()
{
kwic_main();
}
void kwic_main()
{
gen_input();
fd1 = open("File1", O_WRONLY);
do_sort();
close (fd1);
fd2 = open("File1", O_RDONLY);
display_output();
close (fd2);
}
/* Called by gen_input for each new
* input line
*/
void next_input_line( char *line )
{
shift_line( line );
}
/* Called by shift_line for each
* circular shift of its input line
*/
void next_shifted_line( char *line )
{
next_line_to_sort( line );
}
/* Called by do_sort for each final
* sorted line
*/
void next_sorted_line( char *line )
{
write_line(fd1, line);
}
char rline[256];
/* Called by display_output to get next
* line
*/
char *get_next_output_line()
{
read_line(fd2, rline);
return( rline );
}
Implementation 6: Server components
organized in implicit invocation architecture
void kwic_init();
void kwic_main();
main()
{
kwic_init();
kwic_main();
}
/* Semaphores for flow 1 */
int sema1, sema2;
/* Semaphores for flow 2 */
int sema3, sema4;
/* Semaphores for flow 3 */
int sema5, sema6;
/* Carrier resource for flow 1 */
char carrier1[256];
/* Carrier resource for flow 2 */
char carrier2[256];
/* Carrier resource for flow 3 */
char carrier3[256];
kwic_init()
{
/* Initialize semaphore pairs, as
* required
* by the lockstep prerequisite
* coordination
* process, see Figure 4-11
*/
semaphore_init(sema1, 0);
semaphore_init(sema2, 1);
semaphore_init(sema3, 0);
semaphore_init(sema4, 1);
semaphore_init(sema5, 0);
semaphore_init(sema6, 1);
}
/* Write and Read pipe descriptors */
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
/* Global variables used to signal
* shared events
* Set to 1 by generate_event
*/
int ev1;
int ev2;
kwic_main()
{
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
char line1[256];
char line2[256];
int test1;
int test2;
/* Thread #1 (Generate Text) */
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input();
/* Signal end of input */
generate_event(ev1);
goto l99;
l1:
/* Thread #2 (Shift Lines) */
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l2;
l11:
/* Loop here until thread #1 generates
* event
* ev1 to signal no more input lines
*/
semaphore_P(sema1);
/* Read next input line from carrier
* resource */
strcpy(line1, carrier1);
semaphore_V(sema2);
shift_line( line1 );
test1 = check_event( ev1 );
if (!test1) goto l11;
/* Signal end of shifted lines */
generate_event(ev2);
goto l99;
l2:
/* Thread #3 (Sort Lines) */
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
l21:
/* Loop here until thread #2 generates
* event
* ev2 to signal no more shifted lines
*/
semaphore_P(sema4);
/* Read next shifted line from carrier
* resource */
strcpy(line2, carrier2);
semaphore_V(sema3);
next_line_to_sort( line2 );
test2 = check_event( ev2 );
if (!test2) goto l21;
do_sort();
goto l99;
l3:
/* Thread #4 (Display output) */
display_output();
l99:
}
void next_input_line(char *line)
{
semaphore_P(sema2);
/* Write next input line to carrier
* resource */
strcpy(carrier1, line);
semaphore_V(sema1);
}
void next_shifted_line(char *line)
{
semaphore_P(sema4);
/* Write next shifted line to carrier
* resource */
strcpy(carrier2, line);
semaphore_V(sema3);
}
void next_sorted_line( char *line)
{
semaphore_P(sema6);
/* Write next sorted line to carrier
* resource */
strcpy(carrier3, line);
semaphore_V(sema5);
}
char rline[1024];
char *get_next_output_line()
{
semaphore_P(sema5);
/* Read next output line from carrier
* resource */
strcpy( rline, carrier3 );
semaphore_V(sema6);
return( rline );
}
Implementation 7: Mixed components
interconnected using pipes
/* Write and Read pipe descriptors */
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
/* Global variables used to signal
* shared events
* Set to 1 by generate_event
*/
int ev1;
int ev2;
void kwic_main();
main()
{
kwic_main();
}
void kwic_main()
{
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
char line1[256];
int test1;
int test2;
/* Thread #1 (Generate Text) */
create_pipe(&pipe1_w, &pipe1_r);
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input(pipe1_w);
/* Signal end of input to thread #2 */
generate_event(ev1);
goto l99;
l1:
/* Thread #2 (Shift Lines) */
create_pipe(&pipe2_w, &pipe2_r);
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l2;
l11:
/* Loop here until thread #1 generates
* event ev1 to signal end of input
*/
convert_from_byte_stream( pipe1_r, line1 );
shift_line( line1 );
/* Sets test1 to 1 if ev1 != 0 */
test1 = check_event( ev1 );
if (!test1) goto l11;
/* Signal no more lines to thread #3
*/
generate_event(ev2);
goto l99;
l2:
/* Thread #3 (Sort Lines) */
create_pipe(&pipe3_w, &pipe3_r);
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
sort_lines( pipe2_r, pipe3_w )
goto l99;
l3:
/* Thread #4 (Display Output) */
display_output( );
l99:
}
/* Called by USER-WRITTEN
* convert_to_byte_stream
* for each byte of a line
*/
void write_next_byte_in_stream(int pipe, char byte)
{
write( pipe, byte, 1);
}
/* Called by USER-WRITTEN
* convert_from_byte_stream
* to get next byte in stream
*/
char read_next_byte_in_stream(int pipe)
{
char c;
read( pipe, &c, 1);
return( c );
}
/* Called by shift_line for each
* circular shift
* of its input line
*/
void next_shifted_line(char *line)
{
convert_to_byte_stream( pipe2_w, line );
}
char rline[256];
/* Called by display_output to get next
* line
*/
char *get_next_output_line()
{
convert_from_byte_stream( pipe3_r, rline );
return( rline );
}
Implementation 8: Mixed components
organized in main program-subroutine architecture
#include <sys/file.h>
void kwic_main();
main ()
{
kwic_main
}
void kwic_main()
{
/* File descriptors */
int fd1;
int fd2;
int fd3;
int fd4;
int fd5;
int fd6;
char line1[256];
fd1 = open("File1", O_WRONLY);
/* gen_input is a filter and writes
* all output into File1
*/
gen_input( fd1 );
close( fd1 );
fd2 = open("File1", O_RDONLY);
fd3 = open("File2", O_WRONLY);
l11:
/* Loop here until end of File1 */
convert_from_byte_stream( pipe1_r, line1 );
shift_line( line1 );
/* Sets test1 to 1 if end_of_file */
test1 = end_of_file( fd2 );
if (!test1) goto l11;
close( fd2 );
close( fd3 );
fd4 = open("File2", O_RDONLY);
fd5 = open("File3", O_WRONLY);
/* sort_lines is a filter which reads
* input from File2 and writes sorted
* output into File3
*/
sort_lines( fd4, fd5 );
close( fd4 );
close( fd5 );
fd6 = open("File3", O_RDONLY);
display_output( );
close( fd6 );
}
/* Called by USER-WRITTEN
* convert_to_byte_stream
* for each byte of a line
*/
void write_next_byte_in_stream(int pipe, char byte)
{
write( pipe, byte, 1);
}
/* Called by USER-WRITTEN
* convert_from_byte_stream
* to get next byte in stream
*/
char read_next_byte_in_stream(int pipe)
{
char c;
read( pipe, &c, 1);
return( c );
}
/* Called by shift_line for each
* circular shift
* of its input line
*/
void next_shifted_line(char *line)
{
convert_to_byte_stream( fd3, line );
}
char rline[256];
/* Called by display_output to get next
* line
*/
char *get_next_output_line()
{
convert_from_byte_stream( fd6, rline );
return( rline );
}
Implementation 9: Mixed components
organized in implicit invocation architecture
As explained in Section 6.3.2, this implementation introduces
a redundant layer of coordination and is unnecessarily complex
and inefficient. However, it is included here for completeness.
Figure A.2 shows the overall organization of this implementation,
and can help understand the code listing that follows.
/* Write and Read pipe descriptors */
int pipe1_w, pipe1_r;
int pipe2_w, pipe2_r;
int pipe3_w, pipe3_r;
/* Global variables used to signal
* shared events
* Set to 1 by generate_event
*/
int ev1;
int ev2;
int ev3;
int ev4;
/* Semaphores for flow 1 */
int sema1, sema2;
/* Semaphores for flow 2 */
int sema3, sema4;
/* Semaphores for flow 3 */
int sema5, sema6;
/* Carrier resource for flow 1 */
char carrier1;
/* Carrier resource for flow 2 */
char carrier2;
/* Carrier resource for flow 3 */
char carrier3;
void kwic_init();
void kwic_main();
main()
{
kwic_init();
kwic_main();
}
void kwic_init()
{
/* Initialize semaphore pairs, as
* required
* by the lockstep prerequisite
* coordination
* process, see Figure 4-11
*/
semaphore_init(sema1, 0);
semaphore_init(sema2, 1);
semaphore_init(sema3, 0);
semaphore_init(sema4, 1);
semaphore_init(sema5, 0);
semaphore_init(sema6, 1);
}
void kwic_main()
{
int old_t1, new_t1;
int old_t2, new_t2;
int old_t3, new_t3;
int old_t4, new_t4;
int old_t5, new_t5;
int old_t6, new_t6;
char line1[256];
char c1;
char c2;
char c3;
int test1;
int test2;
int test3;
int test4;
/* Thread #1 (Generate Text) */
create_pipe(&pipe1_w, &pipe1_r);
fork_thread(&old_t1, &new_t1);
if (new_t1) goto l1;
gen_input(pipe1_w);
/* Signal end of input to thread #2 */
generate_event(ev1);
goto l99;
l1:
/* Thread #2 */
fork_thread(&old_t2, &new_t2);
if (new_t2) goto l2;
l11:
/* Loop here until thread #1 signals
* end of input
*/
/* Read pipe 1 */
read( pipe1_r, &c1, 1);
/* Write carrier 1 */
semaphore_P(sema2);
carrier1 = c1;
semaphore_V(sema1);
test1 = check_event(ev1);
if (!test1) goto l11;
/* Signal end of input to thread #3 */
generate_event(ev2);
goto l99;
l2:
/* Thread #3 (Shift Lines) */
fork_thread(&old_t3, &new_t3);
if (new_t3) goto l3;
l21:
/* Loop here until thread #2 generates
* event ev2 to signal end of input
*/
convert_to_line( sema1, sema2, &carrier1, line1 );
shift_line( line1 );
test2 = check_event( ev2 );
if (!test2) goto l21;
/* Signal no more lines to thread #4
*/
generate_event(ev3);
goto l99;
l3:
/* Thread #4 */
create_pipe(&pipe2_w, &pipe2_r);
fork_thread(&old_t4, &new_t4);
if (new_t4) goto l4;
l31:
/* Loop here until thread #3 generates
* event ev3 to signal end of input
*/
/* Read carrier 2 */
semaphore_P(sema3);
c2 = carrier2;
semaphore_V(sema4);
/* Write Pipe 2 */
write(pipe2_w, &c2, 1);
test3 = check_event(ev3);
if (!test3) goto l31;
goto l99;
l4:
/* Thread #5 (Sort Lines) */
create_pipe(&pipe3_w, &pipe3_r);
fork_thread(&old_t5, &new_t5);
if (new_t5) goto l5;
sort_lines( pipe2_r, pipe3_w )
generate_event(ev4);
goto l99;
l5:
/* Thread #6 */
fork_thread(&old_t6, &new_t6);
if (new_t6) goto l6;
l51:
/* Loop here until thread #5 signals
* no more sorted lines
*/
/* Read pipe 3 */
read( pipe3_r, &c3, 1);
/* Write carrier 3 */
semaphore_P(sema6);
carrier3 = c3;
semaphore_V(sema5);
test4 = check_event(ev4);
if (!test4) goto l51;
goto l99;
l6:
/* Thread #7 (Display Output) */
display_output( );
l99:
}
/* Called by USER-WRITTEN
* convert_from_line
* for each byte of a line
*/
void write_next_byte_(int sema1, int sema2, char *carrier, char byte)
{
semaphore_P(sema1);
*carrier = byte;
semaphore_V(sema2);
}
/* Called by USER-WRITTEN
* convert_from_byte_stream
* to get next byte in stream
*/
char read_next_byte(int sema1, int sema2, char *carrier)
{
char c;
semaphore_P(sema1);
c = *carrier;
semaphore_V(sema2);
return( c );
}
/* Called by shift_line for each
* circular shift
* of its input line
*/
void next_shifted_line(char *line)
{
convert_from_line( sema4, sema3, &carrier2, line );
}
char rline[256];
/* Called by display_output to get next
* line
*/
char *get_next_output_line()
{
convert_to_line( sema5, sema6, &carrier3, rline
);
return( rline );
}
b. User-written coordination code
/* Convert a null-terminated string into
* a newline-terminated stream of bytes
*
*
*/
void convert_from_line(int sema1, int sema2, char *carrier, char *line)
{
char c;
char *p = line;
while( c = *p++ )
write_next_byte(sema1, sema2, carrier, c);
write_next_byte(sema1, sema2, carrier, '\n');
}
/* Convert a newline-terminated stream
* of bytes
* into a null-terminated string
*
*/
void convert_to_line(int sema1, int sema2, char *carrier, char *line)
{
char *p = line;
char c;
while( (c =
read_next_byte(sema1, sema2, carrier)
!= '\n' )
*p++ = c;
*p = '\0';
}
A.3 Experiment 3: Interactive TEX
System
a. Coordination code automatically generated by SYNTHESIS
Option Explicit
' Global variables used for implementing
' Perishable and Persistent Prerequisites
Global EditorStarted As Integer
Global ViewerStarted As Integer
Global PSValid As Integer
Global LatexDone As Integer
' Global variables used as carrier resources
' in flow dependencies
Global Current_TexFilename As String
Global Current_DviFilename As String
Global Current_PsFilename As String
' Fixed name of replica file used for
' managing Flow 1 (see Section 6.4.2)
Global Const WORD_REPLICA = "d:\tmp.tex"
Sub Main ()
' Initialization
Init
' CONTROLLER
tex_main_control
End Sub
Sub Init ()
' Initialize the global variables used to implement
' persistent and perishable prerequisites
EditorStarted = False
ViewerStarted = False
LatexDone = False
PSValid = False
End Sub
Sub new_document (filename As String)
'Write filename to global variable
Current_TexFilename = filename
'Implement persistent prerequisite using consumer
pull
If EditorStarted Then GoTo editstarted
' START EDITOR
' start_exe0 is a coordination library component that
' starts the specified executable program
start_exe0 "\applic\msoffice\winword\winword.exe"
EditorStarted = True
: editstarted
' Create a replica of filename to manage sharing
' of the .tex file between the editor (Word) and TeX
FileCopy Current_TexFilename, WORD_REPLICA
' OPEN FILE
' gui_sendkeys is a coordination library component that
' formats and sends a key sequence to the specified window
gui_sendkeys1 "Microsoft Word", "^O@1~",
WORD_REPLICA
' DETECT FILE CHANGE
detect_change
End Sub
Sub file_changed ()
Dim f1 As String
Dim f2 As String
Dim f3 As String
'Invalidate .dvi file
LatexDone = False
'Invalidate .ps file
PSValid = False
'Get current filename from global variable
f1 = Current_TexFilename
' Write replica back to original .tex file
FileCopy WORD_REPLICA, f1
' Convert filename to UNIX format expected by latex executable
' (replace backslashes in pathname with slashes)
f2 = Convert_To_Latex(f1)
' CALL LateX
' call_exe1 is a coordination library component that
' starts an executable and waits until it completes
Call_Exe1 "c:\emtex\latex.pif", "LaTeX",
f2
'Enable dvips (Perishable Flow)
LatexDone = True
'Get .dvi filename
f3 = Convert_To_dvi(f2)
' Write to global variable
Current_DviFilename = f3
'Check if dvi driver has started
If ViewerStarted Then GoTo dvistarted
' START VIEWER
' start_exe0 is a coordination library component that
' starts the specified executable program
start_exe1 "\tex\dviwin\dviwin.exe", "-1"
ViewerStarted = True
: dvistarted
'Refresh Dvi driver
start_exe2 "\tex\dviwin\dviwin", "-1",
f3
End Sub
Sub print_document (printer As String)
Dim f1 As String
Dim f2 As String
Dim f3 As String
'If postscript file is not current
If PSValid Then GoTo canprint
'Wait until LaTeX is done
wait_for_event (LatexDone)
'Read latest .dvi filename
f1 = Current_DviFilename
'Convert to postscript
Call_Exe1 "c:\emtex\dvips.pif", "Dvips",
f1
PSValid = True
f2 = Convert_To_ps(f1)
Current_PsFilename = f2
: canprint
'Send postscript to specified printer
f3 = Current_PsFilename
' DOS command:
' copy <filename> <printer_device_name>
' sends a postscript file to a postscript printer
' without further processing
start_exe3 "c:\windows\dosprmpt.pif", "copy",
f3, printer
End Sub
Sub end_application ()
'Quit Dviwin
start_exe1 "c:\tex\dviwin\dviwin", "-1
-c"
'Quit Microsoft Word
gui_sendkeys0 "MicrosoftWord", "%{FX}"
End Sub
b. User-Written Coordination Code
Function Convert_To_Latex (s As String) As String
' The DOS version of TeX (emtex) expects pathnames
' separated by slashes, rather than backslashes.
'
' Also, it does not understand dos drive numbers
' This function converts a dos pathname to the
' equivalent unix pathname
'Replace all \'s by /
Dim f As String
f = ""
Dim i As Integer
For i = 1 To Len(s)
Dim c As String
c = Mid$(s, i, 1)
If c = "\" Then f = f & "/" Else f = f & c
Next i
'Wipe suffix, if any
Dim p As Integer
Dim root As String
p = Len(f)
If Mid$(f, p - 3, 1) = "." Then
root = Left$(f, p - 4)
Else
root = f
End If
'Wipe out drive number, if any
If Len(root) >= 2 And Mid$(root, 2, 1) = ":" Then
root = Mid$(root, 3)
End If
Convert_To_Latex = root
End Function
A.4 Experiment 4: A Collaborative Editor
Toolkit
Coordination code automatically generated by SYNTHESIS
#include <string.h>
/* Global variable defined in each participant
* Set to 1 if another participant has acquired master status
* Set to 0 otherwise
*/
int master_exists;
/* Handler called whenever a participant starts or tries to
* load a file
*/
void read_file(char *filename)
{
/* Broadcast intention to load file to all
* participants. Recipients respond by saving
* their buffer contents.
*
* Call does not return before all recipients
* have responded
*/
DDE_Broadcast0("memNEW");
/* Now load file */
readin(filename, 0);
}
/* Handler called whenever a participant presses the
* "acquire master" key sequence
*/
void master_acquire()
{
/* If no other master, acquire master status */
if (master_exists) goto l1;
m_acq();
l1:
}
/* Handler called whenever a participant presses the
* "release master" key sequence
*/
void master_release()
{
/* If no other master, release master status */
if (master_exists) goto l2;
m_rel();
l2:
}
/* Handler called whenever a participant presses the
* "quit editor" key sequence
*/
void quit_editor()
{
/* If no other master, release master status
* before quitting
*/
if (master_exists) goto l3;
m_rel();
l3:
quit(0, 0);
}
/* Called from within the editor event loop,
* whenever a local event has been detected
*/
void local_event(int event)
{
/* If no other master, send local event */
if (master_exists) goto l4;
m_event(event);
l4:
}
/* Called from within the editor event loop,
* in order to wait for global events
*/
int global_event()
{
int event;
/* Wait until a global event has arrived
* global_in_check and global_in_get
* are coordination library routines for
* accessing the global event queue
* used as part of managing the cumulative
* many-to-many event flow
*/
l5:
if (global_in_check()) goto l6;
WaitMessage();
goto l5;
l6:
event = global_in_get();
return( global_in_get() );
}
/* Called from m_acq */
void master_acquired()
{
/* Broadcast master acquisition */
DDE_Broadcast0("memMACQ");
}
/* Called from m_rel */
void master_released ()
{
/* Broadcast master release */
DDE_Broadcast0("memMREL");
}
/* Called from m_event */
void send_local_event(int event)
{
char cevent[20];
/* Convert to string */
itoa(cevent, event);
/* Broadcast local event */
DDE_Broadcast1("memCHAR, cevent);
}
/* DDE_Broadcast_Handler is defined at each participant editor
* It is called from inside the DDE Callback function
* whenever a WILDCONNECT (broadcast) transaction is detected.
*/
DDE_Broadcast_Handler(char *service, int fSameInst)
/* service is the transaction service name */
/* fSameInst is 1 if we are the same participant who sent the transaction */
{
char *b1;
char *cmd;
char *arg;
int event;
b1 = strchr( service , "|" );
/* All messages that concern me have the format :
cmd|arg
*/
If (b1 == (char *)NULL ) goto l10;
*b1 = '\0';
cmd = service;
arg = (b1 + 1);
/* if no other master exists and a new instance wants to load a file,
* save current buffer contents
*/
if ( !strcmp(cmd, "memNEW") ) goto l11;
if (master_exists) goto l12;
/* Save file */
filesave(0, 0);
l12:
l11:
/* if I received a new character, enter into my input
buffer */
if ( !strcmp( cmd, "memCHAR") ) goto l13;
/* Convert to integer */
event = atoi(arg);
global_in_put(event);
l13:
/* if I received a Master acquire, then set master_exists, unless I am the
* one who is sending it */
if ( !strcmp( cmd, "memMACQ") ) goto l14;
/* Check if I am the one sending the transaction */
if (!fSameInst) goto l15;
master_exists = 1;
l15:
l14:
/* if I received a Master release then no other master
exists */
if ( !strcmp( cmd, "memMREL") ) goto l16;
master_exists = 0;
l16:
l10:
}