Skip to content

Commit d335b68

Browse files
committed
Add README.md
README.md is introduced to help programmers to figure out the usage of coroutine macros.
1 parent bcb985f commit d335b68

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

tinync/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Tinync
2+
Tinync is a simplified version of [nc](https://en.wikipedia.org/wiki/Netcat), which aims to demonstrate the usage of coroutine macros and their effects.
3+
4+
## Usage
5+
### Declaring a Coroutine Function
6+
At the very beginning, a coroutine function should be declared first, coroutine function is part of program that will be executed simultaneously with other coroutines. All coroutines should be specified with the macro `cr_proto`, following shows the example:
7+
```cpp
8+
static void cr_proto(coroutine_name, int x, float y)
9+
{
10+
/* coroutine body */
11+
}
12+
```
13+
Programmers are free to modify the argument list as they need.
14+
15+
### Controlling Macros
16+
With in the coroutine, there are several macros for controlling the behavior of coroutine, following are list of them:
17+
* `cr_begin` aims to initiate the context of coroutine when the coroutine is first invoked, or resume previous execution for the rest of invocations. This macro should be placed before other cotrolling macros except `cr_local`.
18+
* `cr_end` ends up a coroutine that will mark the status of coroutine as finished, which could be detected outside to ensure whether a coroutine finisihed its job or not.
19+
* `cr_wait` is used for waiting a condition happened. Once the condition it is waiting for has not happened yet, it will pause the current coroutine and switch to another.
20+
* `cr_exit` not only yields a coroutine but also updates its state with given state.
21+
* `cr_sys` is a wrapper of `cr_wait` which performs waiting on system calls and other functions that return -1 and set `errno`.
22+
* `cr_local` is a marker for programmers to recognize a variable related to coroutine easily.
23+
In `tinync.c`, we can see the combinations of these macros:
24+
```cpp
25+
static void cr_proto(stdin_loop, byte_queue_t *out)
26+
{
27+
/* b and r are variables used in coroutine whose
28+
* value will be preserved across pauses.
29+
*/
30+
cr_local uint9_t b;
31+
cr_local int r;
32+
33+
cr_begin(); // Initiates the context of this coroutine.
34+
for (;;) {
35+
cr_sys(r = read(STDIN_FILENO, &b, 1)); // Wait for read system call to be success.
36+
if (r == 0) {
37+
cr_wait(cr_queue_empty(out)); // Wait until queue out is flushed.
38+
cr_exit(1); // Exit the coroutine with status update as finished.
39+
}
40+
cr_wait(!cr_queue_full(out)); // Wait until there is place in queue out.
41+
cr_queue_push(out, b);
42+
}
43+
cr_end(); // End up this coroutine, status will be updated as finished.
44+
}
45+
```
46+
47+
### Coroutine Context
48+
There is another important part of coroutine, that is, context. Context of a coroutine will preserve its execution point which could be resumed later. To define a context for a coroutine, use `cr_context` macro and initiates it with macro `cr_context_init`. It is important to **assign an identical name to context and its corresponding function**. With example presented at the beginning, its corresponding context should be specified as follows:
49+
```cpp
50+
cr_context(coroutine_name) = cr_context_init();
51+
```
52+
53+
### Launching and Monitoring
54+
Now, all required preparations are done, programmers may launch a coroutine via `cr_run` macro and monitor it with `cr_status` macro. To execute several coroutines simultaneously, place `cr_run`s that launch coroutines in a loop and keep tracking their status until all of them are finished. Following shows a simple example:
55+
```cpp
56+
while (cr_status(coroutine_1) != CR_FINISHED &&
57+
cr_status(coroutine_2) != CR_FINISHED &&
58+
cr_status(coroutine_3) != CR_FINISHED) {
59+
cr_run(coroutine_1);
60+
cr_run(coroutine_2);
61+
cr_run(coroutine_3);
62+
}
63+
```
64+
65+
## Run the Sample Program
66+
Tinync is a sample program that handles several coroutines to maintain communication with remote while accept user input simultaneously. To compile it, use `make`:
67+
```shell
68+
$ make
69+
```
70+
This sample requires two terminals, let's say `T1` and `T2`. Before launching `tinync`, start `nc` in `T1` first:
71+
```shell
72+
$ nc -l 127.0.0.1 9000
73+
```
74+
Then launch `tinync` in `T2`:
75+
```shell
76+
$ tinync 127.0.0.1 9000
77+
```
78+
Now, any words typed in `T1` will be recived and presented in `T2`, and vice versa.

0 commit comments

Comments
 (0)