Skip to content

Commit 24f98a5

Browse files
committed
Update SharedArrayBuffer + add service-worker attribute details
1 parent 126d9d6 commit 24f98a5

File tree

2 files changed

+127
-75
lines changed

2 files changed

+127
-75
lines changed

docs/faq.md

Lines changed: 24 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ encounter.
4949

5050
### SharedArrayBuffer
5151

52-
This is the first and most common error users may encounter with PyScript.
52+
This is the first and most common error users may encounter with PyScript:
5353

5454
!!! failure
5555

@@ -58,52 +58,28 @@ This is the first and most common error users may encounter with PyScript.
5858
you see this message:
5959

6060
```
61-
Unable to use SharedArrayBuffer due insecure environment.
62-
Please read requirements in MDN: ...
61+
Unable to use `window` or `document` -> https://docs.pyscript.net/latest/faq/#sharedarraybuffer
6362
```
6463

65-
The error contains
66-
[a link to MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer#security_requirements)
67-
but it's the amount of content provided on this topic is overwhelming.
68-
6964
#### When
7065

7166
This error happens when **the server delivering your PyScript application is
72-
incorrectly configured**. It fails to provide the correct headers to handle
73-
security concerns for web workers, or you're not using
74-
[mini-coi](https://github.com/WebReflection/mini-coi#readme) as an alternative
75-
solution. (These requirements are explored
76-
[in the worker page](../user-guide/workers#http-headers).)
77-
78-
**And** at least one of the following scenarios is true:
79-
80-
* There is a `worker` attribute in the *py* or *mpy* script element and the
81-
[sync_main_only](https://pyscript.github.io/polyscript/#extra-config-features)
82-
flag is not present or not `true`.
83-
* There is a `<script type="py-editor">` that uses a *worker* behind the
84-
scenes.
85-
* There is an explicit `PyWorker` or `MPWorker` bootstrapping somewhere in your
86-
code.
87-
88-
!!! info
67+
incorrectly configured** or no *service-worker* attribute fallback
68+
has been provided, hence failing to enable access to `window` and/or `document`
69+
on the *main* thread.
8970

90-
If `sync_main_only` is `true` then interactions between the main thread and
91-
workers are limited to one way calls from the main thread to methods
92-
exposed by workers.
71+
This happens when:
9372

94-
If `sync_main_only = true`, the following caveats apply:
95-
96-
* It is not possible to manipulate the DOM or do anything meaningful on the
97-
main thread **from a worker**. This is because Atomics cannot guarantee
98-
sync-like locks between a worker and the main thread.
99-
* Only a worker's `pyscript.sync` methods are exposed, and they can only be
100-
awaited from the main thread.
101-
* The worker can only `await` main thread references one after the other, so
102-
developer experience is degraded when one needs to interact with the
103-
main thread.
73+
* There is a `worker` attribute in the *py* or *mpy* script element, its code
74+
accesses/imports either `window` or `document` but there's no way to use these
75+
due platform limitations on synchronous *Atomics* operations.
76+
* There is a `<script type="py-editor">` that always uses a *worker* behind the
77+
scenes and no fallback has been provided via `service-worker` attribute.
78+
* There is an explicit `PyWorker` or `MPWorker` bootstrapping somewhere in your
79+
code and no `service_worker` fallback has been provided.
10480

105-
If your project simply bootstraps on the main thread, none of this is relevant
106-
because no worker requires such special features.
81+
All these cases have been documented with code examples and possible solutions
82+
in our [Web Workers](https://docs.pyscript.net/latest/user-guide/workers/) section.
10783

10884
#### Why
10985

@@ -130,13 +106,15 @@ Pyodide related projects, because of its heavier bootstrap or computation
130106
requirements. Using workers ensures the main thread (and thus, the user
131107
interface) remains unblocked.
132108

133-
Unfortunately, due to security concerns and potential attacks to shared
134-
buffers, each server or page needs to allow extra security to prevent malicious
135-
software to read or write into these buffers. But be assured that if you own
136-
your code, your project, and you trust the modules or 3rd party code you need
137-
and use, **there are less likely to be security concerns around this topic
138-
within your project**. This situation is simply an unfortunate "*one rule catch
139-
all*" standard any server can either enable or disable as it pleases.
109+
Unfortunately, we can patch, polyfill, or workaround, these primitives but
110+
we cannot change their intrinsic standard nature and limitations.
111+
112+
We do, however, offer various solutions that circumvent security concerns
113+
around special headers or enable native functionality with relative ease.
114+
115+
Please read our
116+
[Web Workers](https://docs.pyscript.net/latest/user-guide/workers/)
117+
dedicated section to know more about possible solutions.
140118

141119
### Borrowed proxy
142120

docs/user-guide/workers.md

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,18 @@ uses it under the hood.
1414
!!! info
1515

1616
Sometimes you only need to `await` in the main thread the result of a call
17-
to a method exposed in a worker.
17+
to a method exposed in a worker, where neither `window` nor `document` are
18+
either imported or ever needed in such worker.
1819

19-
In such a limited case, and on the understanding that **code in the worker
20-
will not be able to reach back into the main thread**, you should
21-
use the [`sync_main_only` flag](../configuration/#sync_main_only) in your
22-
configuration.
23-
24-
While this eliminates the need for the Atomics related header configuration
25-
(see below), the only possible use case is to **return a serialisable
20+
In these cases, you don't need any special header or *Service Worker*
21+
as long as any worker exposed utility **returns a serializable
2622
result from the method called on the worker**.
2723

2824
## HTTP headers
2925

30-
For Atomics to work **you must ensure your web server enables the following
31-
headers** (this is the default behaviour for
26+
For synchronous Atomics operations to work, those that enable features
27+
such as `window` and `document` from a worker, **you must ensure your web server
28+
enables the following headers** (this is the default behavior for
3229
[pyscript.com](https://pyscript.com)):
3330

3431
```
@@ -38,27 +35,104 @@ Cross-Origin-Embedder-Policy: require-corp
3835
Cross-Origin-Resource-Policy: cross-origin
3936
```
4037

41-
If you are not able to configure your server's headers, use the
42-
[mini-coi](https://github.com/WebReflection/mini-coi#readme) project to
43-
achieve the same end.
44-
45-
!!! Info
46-
47-
The simplest way to use mini-coi is to place the
48-
[mini-coi.js](https://raw.githubusercontent.com/WebReflection/mini-coi/main/mini-coi.js)
49-
file in the root of your website (i.e. `/`), and reference it as the first
50-
child tag in the `<head>` of your HTML documents:
51-
52-
```html
53-
<html>
54-
<head>
55-
<script src="/mini-coi.js" scope="./"></script>
56-
<!-- etc -->
57-
</head>
58-
<!-- etc -->
59-
</html>
38+
If you need those features and you are not able to configure your server's headers,
39+
there are at least 2 options: use the
40+
[mini-coi](https://github.com/WebReflection/mini-coi#readme) project
41+
to enforce server side headers on each request or
42+
use the `service-worker` attribute.
43+
44+
### Option 1: mini-coi
45+
46+
This is still a preferred option, mostly for performance reasons, so that
47+
*Atomics*' synchronous operations can work at native speed.
48+
49+
The simplest way to use mini-coi is to copy the
50+
[mini-coi.js](https://raw.githubusercontent.com/WebReflection/mini-coi/main/mini-coi.js)
51+
file content and save it in the root of your website (i.e. `/`), and reference it
52+
as the first child tag in the `<head>` of your HTML documents:
53+
54+
```html
55+
<html>
56+
<head>
57+
<script src="/mini-coi.js"></script>
58+
<!-- etc -->
59+
</head>
60+
<!-- etc -->
61+
</html>
62+
```
63+
64+
### Option 2: service-worker attribute
65+
66+
Recently introduced, each `<script type="m/py">` or `<m/py-script>` can have
67+
a `service-worker` attribute that must point to a locally served file, the
68+
same way `mini-coi.js` needs to be served, but there is more to it:
69+
70+
* you can chose `mini-coi.js` itself or *any other Service Worker*,
71+
as long as it provides either the right headers to enable *Atomics*
72+
synchronous operations ir it enables
73+
[sabayon polyfill events](https://github.com/WebReflection/sabayon?tab=readme-ov-file#service-worker)
74+
* you can copy and paste
75+
[sabayon Service Worker](https://raw.githubusercontent.com/WebReflection/sabayon/main/dist/sw.js)
76+
into your local project and point at that in the attribute. This will
77+
not change the original behavior of your project, it will not interfere with
78+
all default or pre-defined headers your application use already but it will
79+
fallback to a (slower but working) synchronous operation that would enable
80+
both `window` and `document` access whenever that's needed in your worker logic
81+
82+
```html
83+
<html>
84+
<head>
85+
<!-- PyScript link and script -->
86+
</head>
87+
<body>
88+
<script type="py" service-worker="./sw.js" worker>
89+
from pyscript import window, document
90+
91+
document.body.append("Hello PyScript!")
92+
</script>
93+
</body>
94+
</html>
95+
```
96+
97+
!!! note
98+
99+
Using the *sabayon* fallback to enable *Atomics* synchronous operations
100+
should be the least solution to consider because it is inevitably
101+
slower than having native operations enabled by other means.
102+
103+
When that is still needed or desired, it is always better to reduce
104+
the amount of synchronous operations by caching references from the
105+
*main* thread.
106+
107+
```python
108+
# ❌ THIS IS UNNECESSARILY SLOWER
109+
from pyscript import document
110+
111+
# add a data-test="not ideal attribute"
112+
document.body.dataset.test = "not ideal"
113+
# read a data-test attribute
114+
print(document.body.dataset.test)
115+
116+
# - - - - - - - - - - - - - - - - - - - - -
117+
118+
# ✔️ THIS IS FINE
119+
from pyscript import document
120+
121+
# if needed elsewhere, reach it once
122+
body = document.body
123+
dataset = body.dataset
124+
125+
# add a data-test="not ideal attribute"
126+
dataset.test = "not ideal"
127+
# read a data-test attribute
128+
print(dataset.test)
60129
```
61130

131+
In latter example the amount of operations have been reduced
132+
from *6* to just *4* and the rule of thumb is:
133+
if you ever need a *DOM* reference more than once, cache it 👍
134+
135+
62136
## Start working
63137

64138
To start your code in a worker, simply ensure the `<script>`, `<py-script>` or

0 commit comments

Comments
 (0)