Asynchronous Numbers Station
/* Mystify the client at the other end of the specified client socket by sending
* some cryptic numbers.
*/
Method "Mystify_" is
[
client : client socket
|
rng ::= a Mersenne Twister;
sem ::= a semaphore named "done" with 0 out of 1 permits;
Guard
[
killer : exception;
/* Allow fast reuse of the local address. */
client's address reusability := true;
/* Send the size of the stream of mysterious numbers. */
howMany ::= 5 × rng's next [1..51];
Ignore: a fiber writing <howMany> to client,
on success doing [Signal sem;],
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
If killer is assigned then [Raise killer];
/* Send some mysterious numbers. */
weirdNumbers ::= map 1 to howMany through [i : byte | rng's next byte];
Ignore: a fiber writing weirdNumbers to client,
on success doing [Signal sem;],
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
If killer is assigned then [Raise killer];
]
ensure [Close client;];
] : ⊤;
/* At the present time, the spaces are required in the specification of an IP
* address. This limitation will be removed once Avail has a backtracking
* lexical scanner.
*
* Incidentally, port 0 represents a wildcard port; the system will choose a
* suitable one.
*/
serverAddress ::= 127 . 0 . 0 . 1 : 0;
/* Run the numbers station. The server stops automatically after mystifying
* three clients.
*/
Method "Start Lincolnshire Poacher" is
[
sem ::= a semaphore named "not accepting" with 0 out of 1 permits;
server ::= a server socket named "Lincolnshire Poacher";
Guard
[
server's address reusability := true;
Bind server to serverAddress;
Repeat
[
killer : exception;
Ignore: a fiber waiting until server accepts a new connection,
on success doing [client : client socket | Signal sem; Mystify client;]
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
If killer is assigned then [Raise killer];
] 3 times;
]
ensure [Close server;]
] : ⊤;
/* Connect to the server, receive the mysterious numbers, and regurgitate them
* to standard output as hyphen-separated quintets.
*/
Method "Be mystified" is
[
sem ::= a semaphore named "done" with 0 out of 1 permits;
client ::= a client socket named "amateur spy";
Guard
[
client's address reusability := true;
Bind client to 127 . 0 . 0 . 1 : 0;
Ignore: a fiber connecting client to serverAddress,
on success doing [Signal sem;],
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
If killer is assigned then [Raise killer];
expected : byte;
Ignore: a fiber reading 1 byte from client,
on success doing
[
justRead : <byte…|>,
endOfStream : boolean
|
expected := justRead[1];
Signal sem;
]
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
If killer is assigned then [Raise killer];
Assert: expected is assigned;
weirdNumbers : <byte…|> := <>;
Until
[
Ignore: a fiber reading (expected - |weirdNumbers|) bytes from client,
on success doing
[
justRead : <byte…|>,
endOfStream : boolean
|
weirdNumbers := weirdNumbers ++ justRead;
Signal sem;
]
on failure doing [e : exception | killer := e; Signal sem;];
Await sem;
killer is assigned ∨ [expected = |weirdNumbers|]
];
If killer is assigned then [Raise killer];
/* Report the mysterious numbers. */
quintets ::= map 1 to |weirdNumbers| - 1 by 5 through
[i : byte | weirdNumbers[i..i + 4]];
For each of quintets do
[
q : <byte…|5>
|
Print: format "“①”-“②”-“③”-“④”-“⑤”\n" with unchecked q;
];
]
ensure [Close client;];
] : ⊤;
/* Run it! */
Start Lincolnshire Poacher;
Repeat [Be mystified;] 3 times;
Return to Avail Newsletters, Edition #3 |