commit 73efecca42dbc44200e797e02f8c1e24fcff26ef
parent 2a8e7aeeef99161b6f0378ac14e297c23ca6227b
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date: Thu, 5 Jun 2025 10:13:28 +0200
Added two more examples
Diffstat:
19 files changed, 250 insertions(+), 2 deletions(-)
diff --git a/03_threads/index.html b/03_threads/index.html
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
- <title>Multiply two numbers</title>
+ <title>Primes in a range</title>
<script src="./script.mjs" type="module" defer></script>
</head>
diff --git a/03_threads/program.mjs b/03_threads/program.mjs
@@ -3,4 +3,4 @@ import Primes from "./build/primes.mjs"
var primes = await Primes();
const count = primes._primes_in_range(1, 100);
-console.log("There are " + count + " primes betwees 1 and 100");
+console.log("There are " + count + " primes between 1 and 100");
diff --git a/04_no_block/build.sh b/04_no_block/build.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+mkdir -p build
+emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \
+ -pthread -sPTHREAD_POOL_SIZE=16 \
+ -o build/primes.mjs primes.c
diff --git a/04_no_block/index.html b/04_no_block/index.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html lang="en-US">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Primes in a range</title>
+ <script src="./script.mjs" type="module" defer></script>
+</head>
+
+<body>
+ <label for="aInput">Lower bound (included)</label><br />
+ <input id="aInput" name="aInput" /> x <br />
+ <label for="bInput">Upper bound (excluded)</label><br />
+ <input id="bInput" name="bInput" /> <br />
+ <button id="goButton">Compute</button> <br />
+ <p id="resultText"></p>
+</body>
+
+</html>
diff --git a/04_no_block/mime.txt 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
@@ -0,0 +1,53 @@
+#include <stdbool.h>
+#include <pthread.h>
+
+#define NTHREADS 16
+
+bool isprime(int);
+void *pthread_routine(void *);
+
+struct interval { int low; int high; int count; };
+
+int primes_in_range(int low, int high) {
+ pthread_t threads[NTHREADS];
+ struct interval args[NTHREADS];
+
+ if (low < 0 || high < low)
+ return 0;
+
+ int interval_size = (high-low)/NTHREADS + 1;
+ for (int i = 0; i < NTHREADS; i++) {
+ args[i].low = low + i*interval_size;
+ args[i].high = args[i].low + interval_size;
+ pthread_create(&threads[i], NULL, pthread_routine, &args[i]);
+ }
+
+ int result = 0;
+ for (int i = 0; i < NTHREADS; i++) {
+ pthread_join(threads[i], NULL);
+ result += args[i].count;
+ }
+
+ return result;
+}
+
+bool isprime(int n) {
+ if (n < 2)
+ return false;
+
+ for (int i = 2; i*i <= n; i++)
+ if (n % i == 0)
+ return false;
+ return true;
+}
+
+void *pthread_routine(void *arg) {
+ struct interval *interval = arg;
+
+ interval->count = 0;
+ for (int i = interval->low; i < interval->high; i++)
+ if (isprime(i))
+ interval->count++;
+
+ return NULL;
+}
diff --git a/04_no_block/run-server.sh b/04_no_block/run-server.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+darkhttpd . \
+ --mimetypes mime.txt \
+ --header 'Cross-Origin-Opener-Policy: same-origin' \
+ --header 'Cross-Origin-Embedder-Policy: require-corp'
diff --git a/04_no_block/script.mjs b/04_no_block/script.mjs
@@ -0,0 +1,14 @@
+var aInput = document.getElementById("aInput");
+var bInput = document.getElementById("bInput");
+var button = document.getElementById("goButton");
+var resultText = document.getElementById("resultText");
+
+var worker = new Worker("./worker.mjs", { type: "module" });
+
+button.addEventListener("click", () => worker.postMessage({
+ a: Number(aInput.value),
+ b: Number(bInput.value)
+}));
+
+worker.onmessage = (e) => resultText.innerText = "There are " +
+ 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
@@ -0,0 +1,8 @@
+import Primes from "./build/primes.mjs";
+
+var primes = await Primes();
+
+onmessage = (e) => {
+ const count = primes._primes_in_range(e.data.a, e.data.b);
+ postMessage({ result: count, a: e.data.a, b: e.data.b });
+};
diff --git a/05_callback/build.sh b/05_callback/build.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+mkdir -p build
+emcc -sEXPORTED_FUNCTIONS=_primes_in_range -sMODULARIZE -sEXPORT_NAME=Primes \
+ -sEXPORTED_RUNTIME_METHODS=addFunction,UTF8ToString \
+ -sALLOW_TABLE_GROWTH \
+ -pthread -sPTHREAD_POOL_SIZE=16 \
+ -o build/primes.mjs primes.c
diff --git a/05_callback/index.html b/05_callback/index.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html lang="en-US">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>Primes in a range</title>
+ <script src="./script.mjs" type="module" defer></script>
+</head>
+
+<body>
+ <label for="aInput">Lower bound (included)</label><br />
+ <input id="aInput" name="aInput" /> x <br />
+ <label for="bInput">Upper bound (excluded)</label><br />
+ <input id="bInput" name="bInput" /> <br />
+ <button id="goButton">Compute</button> <br />
+ <p id="resultText"></p>
+</body>
+
+</html>
diff --git a/05_callback/mime.txt b/05_callback/mime.txt
@@ -0,0 +1 @@
+text/javascript mjs
diff --git a/05_callback/primes.c b/05_callback/primes.c
@@ -0,0 +1,55 @@
+#include <stdbool.h>
+#include <pthread.h>
+
+#define NTHREADS 16
+
+bool isprime(int);
+void *pthread_routine(void *);
+
+struct interval { int low; int high; int count; };
+
+int primes_in_range(int low, int high, void (*log)(const char *)) {
+ pthread_t threads[NTHREADS];
+ struct interval args[NTHREADS];
+
+ if (low < 0 || high < low)
+ return 0;
+
+ int interval_size = (high-low)/NTHREADS + 1;
+ for (int i = 0; i < NTHREADS; i++) {
+ args[i].low = low + i*interval_size;
+ args[i].high = args[i].low + interval_size;
+ pthread_create(&threads[i], NULL, pthread_routine, &args[i]);
+ }
+
+ log("All threads have started, computing...");
+
+ int result = 0;
+ for (int i = 0; i < NTHREADS; i++) {
+ pthread_join(threads[i], NULL);
+ result += args[i].count;
+ }
+
+ return result;
+}
+
+bool isprime(int n) {
+ if (n < 2)
+ return false;
+
+ for (int i = 2; i*i <= n; i++)
+ if (n % i == 0)
+ return false;
+ return true;
+}
+
+void *pthread_routine(void *arg) {
+ struct interval *interval = arg;
+
+ interval->count = 0;
+ for (int i = interval->low; i < interval->high; i++)
+ if (isprime(i))
+ interval->count++;
+
+ return NULL;
+}
diff --git a/05_callback/program.mjs b/05_callback/program.mjs
@@ -0,0 +1,11 @@
+import Primes from "./build/primes.mjs"
+
+var primes = await Primes();
+const logPtr = primes.addFunction((cstr) => {
+ console.log(primes.UTF8ToString(cstr));
+}, 'vp');
+
+const a = 1;
+const b = 10000000;
+const count = primes._primes_in_range(a, b, logPtr);
+console.log("There are " + count + " primes between " + a + " and " + b);
diff --git a/05_callback/run-node.sh b/05_callback/run-node.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+node program.mjs
diff --git a/05_callback/run-server.sh b/05_callback/run-server.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+darkhttpd . \
+--port 8082 \
+ --mimetypes mime.txt \
+ --header 'Cross-Origin-Opener-Policy: same-origin' \
+ --header 'Cross-Origin-Embedder-Policy: require-corp'
diff --git a/05_callback/script.mjs b/05_callback/script.mjs
@@ -0,0 +1,13 @@
+var aInput = document.getElementById("aInput");
+var bInput = document.getElementById("bInput");
+var button = document.getElementById("goButton");
+var resultText = document.getElementById("resultText");
+
+var worker = new Worker("./worker.mjs", { type: "module" });
+
+button.addEventListener("click", () => worker.postMessage({
+ a: Number(aInput.value),
+ b: Number(bInput.value)
+}));
+
+worker.onmessage = (e) => resultText.innerText = e.data.message;
diff --git a/05_callback/worker.mjs b/05_callback/worker.mjs
@@ -0,0 +1,12 @@
+import Primes from "./build/primes.mjs";
+
+var primes = await Primes();
+const logPtr = primes.addFunction((cstr) => {
+ postMessage({ message: primes.UTF8ToString(cstr) });
+}, "vp");
+
+onmessage = (e) => {
+ const count = primes._primes_in_range(e.data.a, e.data.b, logPtr);
+ postMessage({ message: "There are " + count + " primes between " +
+ e.data.a + " and " + e.data.b });
+};
diff --git a/README.md b/README.md
@@ -24,6 +24,9 @@ scripts, for convenience:
[localhost:8080](http://localhost:8080) running an example web page
(require darkhttpd, see below).
+If one of the `run-*` scripts is missing, it means that that example
+is only meant to run in Node.js, or only as a script in a web page.
+
The examples have been tested only on Linux, but should work on any
UNIX system, and should be easy to adapt to Windows or other OSes.
Pull requests are welcome.
@@ -63,3 +66,12 @@ In `03_threads` we build a more complicated example based on
[pthreads](https://en.wikipedia.org/wiki/Pthreads). To run this
example, the web server has to be configured to provide the correct
`Cross-Origin-*` headers, see `03_threads/run-server.sh` for details.
+
+## 4. Don't block the main thread
+
+In `04_no_block` we avoid our calculations blocking the main browser
+thread by using a web worker.
+
+## 5. Callback functions
+
+In `05_callback` the example is extended to include a callback function.