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;










Figure A.1: Implementation 3 (Pipe components in implicit invocation organization)

/* 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;





Figure A.2: Implementation 9 (Mixed components in implicit invocation organization)

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:

}


Continue on to the Addendum