diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-05 10:13:28 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-06-05 10:13:28 +0200 |
| commit | 73efecca42dbc44200e797e02f8c1e24fcff26ef (patch) | |
| tree | 46969fed9d5c09a4164257dace56a71cad9092ab | |
| parent | 2a8e7aeeef99161b6f0378ac14e297c23ca6227b (diff) | |
| download | emscripten-tutorial-73efecca42dbc44200e797e02f8c1e24fcff26ef.tar.gz emscripten-tutorial-73efecca42dbc44200e797e02f8c1e24fcff26ef.zip | |
Added two more examples
| -rw-r--r-- | 03_threads/index.html | 2 | ||||
| -rw-r--r-- | 03_threads/program.mjs | 2 | ||||
| -rwxr-xr-x | 04_no_block/build.sh | 6 | ||||
| -rw-r--r-- | 04_no_block/index.html | 19 | ||||
| -rw-r--r-- | 04_no_block/mime.txt | 1 | ||||
| -rw-r--r-- | 04_no_block/primes.c | 53 | ||||
| -rwxr-xr-x | 04_no_block/run-server.sh | 6 | ||||
| -rw-r--r-- | 04_no_block/script.mjs | 14 | ||||
| -rw-r--r-- | 04_no_block/worker.mjs | 8 | ||||
| -rwxr-xr-x | 05_callback/build.sh | 8 | ||||
| -rw-r--r-- | 05_callback/index.html | 19 | ||||
| -rw-r--r-- | 05_callback/mime.txt | 1 | ||||
| -rw-r--r-- | 05_callback/primes.c | 55 | ||||
| -rw-r--r-- | 05_callback/program.mjs | 11 | ||||
| -rwxr-xr-x | 05_callback/run-node.sh | 3 | ||||
| -rwxr-xr-x | 05_callback/run-server.sh | 7 | ||||
| -rw-r--r-- | 05_callback/script.mjs | 13 | ||||
| -rw-r--r-- | 05_callback/worker.mjs | 12 | ||||
| -rw-r--r-- | README.md | 12 |
19 files changed, 250 insertions, 2 deletions
diff --git a/03_threads/index.html b/03_threads/index.html index 29cfaf8..7a1fba7 100644 --- a/03_threads/index.html +++ b/03_threads/index.html | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8" /> | 4 | <meta charset="utf-8" /> |
| 5 | <meta name="viewport" content="width=device-width" /> | 5 | <meta name="viewport" content="width=device-width" /> |
| 6 | <title>Multiply two numbers</title> | 6 | <title>Primes in a range</title> |
| 7 | <script src="./script.mjs" type="module" defer></script> | 7 | <script src="./script.mjs" type="module" defer></script> |
| 8 | </head> | 8 | </head> |
| 9 | 9 | ||
diff --git a/03_threads/program.mjs b/03_threads/program.mjs index a5a89ce..db0a2c6 100644 --- a/03_threads/program.mjs +++ b/03_threads/program.mjs | |||
| @@ -3,4 +3,4 @@ import Primes from "./build/primes.mjs" | |||
| 3 | var primes = await Primes(); | 3 | var primes = await Primes(); |
| 4 | 4 | ||
| 5 | const count = primes._primes_in_range(1, 100); | 5 | const count = primes._primes_in_range(1, 100); |
| 6 | console.log("There are " + count + " primes betwees 1 and 100"); | 6 | console.log("There are " + count + " primes between 1 and 100"); |
diff --git a/04_no_block/build.sh b/04_no_block/build.sh new file mode 100755 index 0000000..2f08909 --- /dev/null +++ b/04_no_block/build.sh | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | mkdir -p build | ||
| 4 | emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \ | ||
| 5 | -pthread -sPTHREAD_POOL_SIZE=16 \ | ||
| 6 | -o build/primes.mjs primes.c | ||
diff --git a/04_no_block/index.html b/04_no_block/index.html new file mode 100644 index 0000000..7a1fba7 --- /dev/null +++ b/04_no_block/index.html | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en-US"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8" /> | ||
| 5 | <meta name="viewport" content="width=device-width" /> | ||
| 6 | <title>Primes in a range</title> | ||
| 7 | <script src="./script.mjs" type="module" defer></script> | ||
| 8 | </head> | ||
| 9 | |||
| 10 | <body> | ||
| 11 | <label for="aInput">Lower bound (included)</label><br /> | ||
| 12 | <input id="aInput" name="aInput" /> x <br /> | ||
| 13 | <label for="bInput">Upper bound (excluded)</label><br /> | ||
| 14 | <input id="bInput" name="bInput" /> <br /> | ||
| 15 | <button id="goButton">Compute</button> <br /> | ||
| 16 | <p id="resultText"></p> | ||
| 17 | </body> | ||
| 18 | |||
| 19 | </html> | ||
diff --git a/04_no_block/mime.txt b/04_no_block/mime.txt new file mode 100644 index 0000000..6a9a425 --- /dev/null +++ b/04_no_block/mime.txt | |||
| @@ -0,0 +1 @@ | |||
| text/javascript mjs | |||
diff --git a/04_no_block/primes.c b/04_no_block/primes.c new file mode 100644 index 0000000..a5076c6 --- /dev/null +++ b/04_no_block/primes.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #include <stdbool.h> | ||
| 2 | #include <pthread.h> | ||
| 3 | |||
| 4 | #define NTHREADS 16 | ||
| 5 | |||
| 6 | bool isprime(int); | ||
| 7 | void *pthread_routine(void *); | ||
| 8 | |||
| 9 | struct interval { int low; int high; int count; }; | ||
| 10 | |||
| 11 | int primes_in_range(int low, int high) { | ||
| 12 | pthread_t threads[NTHREADS]; | ||
| 13 | struct interval args[NTHREADS]; | ||
| 14 | |||
| 15 | if (low < 0 || high < low) | ||
| 16 | return 0; | ||
| 17 | |||
| 18 | int interval_size = (high-low)/NTHREADS + 1; | ||
| 19 | for (int i = 0; i < NTHREADS; i++) { | ||
| 20 | args[i].low = low + i*interval_size; | ||
| 21 | args[i].high = args[i].low + interval_size; | ||
| 22 | pthread_create(&threads[i], NULL, pthread_routine, &args[i]); | ||
| 23 | } | ||
| 24 | |||
| 25 | int result = 0; | ||
| 26 | for (int i = 0; i < NTHREADS; i++) { | ||
| 27 | pthread_join(threads[i], NULL); | ||
| 28 | result += args[i].count; | ||
| 29 | } | ||
| 30 | |||
| 31 | return result; | ||
| 32 | } | ||
| 33 | |||
| 34 | bool isprime(int n) { | ||
| 35 | if (n < 2) | ||
| 36 | return false; | ||
| 37 | |||
| 38 | for (int i = 2; i*i <= n; i++) | ||
| 39 | if (n % i == 0) | ||
| 40 | return false; | ||
| 41 | return true; | ||
| 42 | } | ||
| 43 | |||
| 44 | void *pthread_routine(void *arg) { | ||
| 45 | struct interval *interval = arg; | ||
| 46 | |||
| 47 | interval->count = 0; | ||
| 48 | for (int i = interval->low; i < interval->high; i++) | ||
| 49 | if (isprime(i)) | ||
| 50 | interval->count++; | ||
| 51 | |||
| 52 | return NULL; | ||
| 53 | } | ||
diff --git a/04_no_block/run-server.sh b/04_no_block/run-server.sh new file mode 100755 index 0000000..3a526f3 --- /dev/null +++ b/04_no_block/run-server.sh | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | darkhttpd . \ | ||
| 4 | --mimetypes mime.txt \ | ||
| 5 | --header 'Cross-Origin-Opener-Policy: same-origin' \ | ||
| 6 | --header 'Cross-Origin-Embedder-Policy: require-corp' | ||
diff --git a/04_no_block/script.mjs b/04_no_block/script.mjs new file mode 100644 index 0000000..b9c27ea --- /dev/null +++ b/04_no_block/script.mjs | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | var aInput = document.getElementById("aInput"); | ||
| 2 | var bInput = document.getElementById("bInput"); | ||
| 3 | var button = document.getElementById("goButton"); | ||
| 4 | var resultText = document.getElementById("resultText"); | ||
| 5 | |||
| 6 | var worker = new Worker("./worker.mjs", { type: "module" }); | ||
| 7 | |||
| 8 | button.addEventListener("click", () => worker.postMessage({ | ||
| 9 | a: Number(aInput.value), | ||
| 10 | b: Number(bInput.value) | ||
| 11 | })); | ||
| 12 | |||
| 13 | worker.onmessage = (e) => resultText.innerText = "There are " + | ||
| 14 | e.data.result + " primes between " + e.data.a + " and " + e.data.b; | ||
diff --git a/04_no_block/worker.mjs b/04_no_block/worker.mjs new file mode 100644 index 0000000..6476203 --- /dev/null +++ b/04_no_block/worker.mjs | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | import Primes from "./build/primes.mjs"; | ||
| 2 | |||
| 3 | var primes = await Primes(); | ||
| 4 | |||
| 5 | onmessage = (e) => { | ||
| 6 | const count = primes._primes_in_range(e.data.a, e.data.b); | ||
| 7 | postMessage({ result: count, a: e.data.a, b: e.data.b }); | ||
| 8 | }; | ||
diff --git a/05_callback/build.sh b/05_callback/build.sh new file mode 100755 index 0000000..998ebd1 --- /dev/null +++ b/05_callback/build.sh | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | mkdir -p build | ||
| 4 | emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \ | ||
| 5 | -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString \ | ||
| 6 | -sALLOW_TABLE_GROWTH \ | ||
| 7 | -pthread -sPTHREAD_POOL_SIZE=16 \ | ||
| 8 | -o build/primes.mjs primes.c | ||
diff --git a/05_callback/index.html b/05_callback/index.html new file mode 100644 index 0000000..7a1fba7 --- /dev/null +++ b/05_callback/index.html | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | <!doctype html> | ||
| 2 | <html lang="en-US"> | ||
| 3 | <head> | ||
| 4 | <meta charset="utf-8" /> | ||
| 5 | <meta name="viewport" content="width=device-width" /> | ||
| 6 | <title>Primes in a range</title> | ||
| 7 | <script src="./script.mjs" type="module" defer></script> | ||
| 8 | </head> | ||
| 9 | |||
| 10 | <body> | ||
| 11 | <label for="aInput">Lower bound (included)</label><br /> | ||
| 12 | <input id="aInput" name="aInput" /> x <br /> | ||
| 13 | <label for="bInput">Upper bound (excluded)</label><br /> | ||
| 14 | <input id="bInput" name="bInput" /> <br /> | ||
| 15 | <button id="goButton">Compute</button> <br /> | ||
| 16 | <p id="resultText"></p> | ||
| 17 | </body> | ||
| 18 | |||
| 19 | </html> | ||
diff --git a/05_callback/mime.txt b/05_callback/mime.txt new file mode 100644 index 0000000..6a9a425 --- /dev/null +++ b/05_callback/mime.txt | |||
| @@ -0,0 +1 @@ | |||
| text/javascript mjs | |||
diff --git a/05_callback/primes.c b/05_callback/primes.c new file mode 100644 index 0000000..f581161 --- /dev/null +++ b/05_callback/primes.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #include <stdbool.h> | ||
| 2 | #include <pthread.h> | ||
| 3 | |||
| 4 | #define NTHREADS 16 | ||
| 5 | |||
| 6 | bool isprime(int); | ||
| 7 | void *pthread_routine(void *); | ||
| 8 | |||
| 9 | struct interval { int low; int high; int count; }; | ||
| 10 | |||
| 11 | int primes_in_range(int low, int high, void (*log)(const char *)) { | ||
| 12 | pthread_t threads[NTHREADS]; | ||
| 13 | struct interval args[NTHREADS]; | ||
| 14 | |||
| 15 | if (low < 0 || high < low) | ||
| 16 | return 0; | ||
| 17 | |||
| 18 | int interval_size = (high-low)/NTHREADS + 1; | ||
| 19 | for (int i = 0; i < NTHREADS; i++) { | ||
| 20 | args[i].low = low + i*interval_size; | ||
| 21 | args[i].high = args[i].low + interval_size; | ||
| 22 | pthread_create(&threads[i], NULL, pthread_routine, &args[i]); | ||
| 23 | } | ||
| 24 | |||
| 25 | log("All threads have started, computing..."); | ||
| 26 | |||
| 27 | int result = 0; | ||
| 28 | for (int i = 0; i < NTHREADS; i++) { | ||
| 29 | pthread_join(threads[i], NULL); | ||
| 30 | result += args[i].count; | ||
| 31 | } | ||
| 32 | |||
| 33 | return result; | ||
| 34 | } | ||
| 35 | |||
| 36 | bool isprime(int n) { | ||
| 37 | if (n < 2) | ||
| 38 | return false; | ||
| 39 | |||
| 40 | for (int i = 2; i*i <= n; i++) | ||
| 41 | if (n % i == 0) | ||
| 42 | return false; | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | void *pthread_routine(void *arg) { | ||
| 47 | struct interval *interval = arg; | ||
| 48 | |||
| 49 | interval->count = 0; | ||
| 50 | for (int i = interval->low; i < interval->high; i++) | ||
| 51 | if (isprime(i)) | ||
| 52 | interval->count++; | ||
| 53 | |||
| 54 | return NULL; | ||
| 55 | } | ||
diff --git a/05_callback/program.mjs b/05_callback/program.mjs new file mode 100644 index 0000000..fd85a58 --- /dev/null +++ b/05_callback/program.mjs | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | import Primes from "./build/primes.mjs" | ||
| 2 | |||
| 3 | var primes = await Primes(); | ||
| 4 | const logPtr = primes.addFunction((cstr) => { | ||
| 5 | console.log(primes.UTF8ToString(cstr)); | ||
| 6 | }, 'vp'); | ||
| 7 | |||
| 8 | const a = 1; | ||
| 9 | const b = 10000000; | ||
| 10 | const count = primes._primes_in_range(a, b, logPtr); | ||
| 11 | console.log("There are " + count + " primes between " + a + " and " + b); | ||
diff --git a/05_callback/run-node.sh b/05_callback/run-node.sh new file mode 100755 index 0000000..a6c9aa8 --- /dev/null +++ b/05_callback/run-node.sh | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | node program.mjs | ||
diff --git a/05_callback/run-server.sh b/05_callback/run-server.sh new file mode 100755 index 0000000..6e75760 --- /dev/null +++ b/05_callback/run-server.sh | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | darkhttpd . \ | ||
| 4 | --port 8082 \ | ||
| 5 | --mimetypes mime.txt \ | ||
| 6 | --header 'Cross-Origin-Opener-Policy: same-origin' \ | ||
| 7 | --header 'Cross-Origin-Embedder-Policy: require-corp' | ||
diff --git a/05_callback/script.mjs b/05_callback/script.mjs new file mode 100644 index 0000000..9fce6fe --- /dev/null +++ b/05_callback/script.mjs | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | var aInput = document.getElementById("aInput"); | ||
| 2 | var bInput = document.getElementById("bInput"); | ||
| 3 | var button = document.getElementById("goButton"); | ||
| 4 | var resultText = document.getElementById("resultText"); | ||
| 5 | |||
| 6 | var worker = new Worker("./worker.mjs", { type: "module" }); | ||
| 7 | |||
| 8 | button.addEventListener("click", () => worker.postMessage({ | ||
| 9 | a: Number(aInput.value), | ||
| 10 | b: Number(bInput.value) | ||
| 11 | })); | ||
| 12 | |||
| 13 | worker.onmessage = (e) => resultText.innerText = e.data.message; | ||
diff --git a/05_callback/worker.mjs b/05_callback/worker.mjs new file mode 100644 index 0000000..736e9f8 --- /dev/null +++ b/05_callback/worker.mjs | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | import Primes from "./build/primes.mjs"; | ||
| 2 | |||
| 3 | var primes = await Primes(); | ||
| 4 | const logPtr = primes.addFunction((cstr) => { | ||
| 5 | postMessage({ message: primes.UTF8ToString(cstr) }); | ||
| 6 | }, "vp"); | ||
| 7 | |||
| 8 | onmessage = (e) => { | ||
| 9 | const count = primes._primes_in_range(e.data.a, e.data.b, logPtr); | ||
| 10 | postMessage({ message: "There are " + count + " primes between " + | ||
| 11 | e.data.a + " and " + e.data.b }); | ||
| 12 | }; | ||
| @@ -24,6 +24,9 @@ scripts, for convenience: | |||
| 24 | [localhost:8080](http://localhost:8080) running an example web page | 24 | [localhost:8080](http://localhost:8080) running an example web page |
| 25 | (require darkhttpd, see below). | 25 | (require darkhttpd, see below). |
| 26 | 26 | ||
| 27 | If one of the `run-*` scripts is missing, it means that that example | ||
| 28 | is only meant to run in Node.js, or only as a script in a web page. | ||
| 29 | |||
| 27 | The examples have been tested only on Linux, but should work on any | 30 | The examples have been tested only on Linux, but should work on any |
| 28 | UNIX system, and should be easy to adapt to Windows or other OSes. | 31 | UNIX system, and should be easy to adapt to Windows or other OSes. |
| 29 | Pull requests are welcome. | 32 | Pull requests are welcome. |
| @@ -63,3 +66,12 @@ In `03_threads` we build a more complicated example based on | |||
| 63 | [pthreads](https://en.wikipedia.org/wiki/Pthreads). To run this | 66 | [pthreads](https://en.wikipedia.org/wiki/Pthreads). To run this |
| 64 | example, the web server has to be configured to provide the correct | 67 | example, the web server has to be configured to provide the correct |
| 65 | `Cross-Origin-*` headers, see `03_threads/run-server.sh` for details. | 68 | `Cross-Origin-*` headers, see `03_threads/run-server.sh` for details. |
| 69 | |||
| 70 | ## 4. Don't block the main thread | ||
| 71 | |||
| 72 | In `04_no_block` we avoid our calculations blocking the main browser | ||
| 73 | thread by using a web worker. | ||
| 74 | |||
| 75 | ## 5. Callback functions | ||
| 76 | |||
| 77 | In `05_callback` the example is extended to include a callback function. | ||
