-
Notifications
You must be signed in to change notification settings - Fork 5
-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Restricting shared memory reads and writes to unsafe
contexts
#26
Comments
unsafe
contextunsafe
contexts
I like the lexical semantics of this a lot. Bikeshedding-wise, |
Writing down comments from the working session: It's not clear that the addition of additional unsafe contexts (beyond the bare Given that function colouring is an explicit non-goal of this proposal, I would further argue that This proposal is inspired by Rust's unsafe keyword. But note that in Rust, For example, this version of unsafe functions enables idioms like As a very different language from Rust, it's unclear whether JS would benefit from a similar kind of function colouring. By restricting the initial proposal to only support unsafe blocks, we leave flexibility to decide based on user experience whether we want function colouring or not. For these reasons, I think we should remove "Other unsafe contexts" from this proposal. |
During the last Shared Structs call we discussed concerns raised about there being insufficient guardrails in place to prevent users from inadvertently introducing data races by interacting with shared data structures outside of some mechanism to synchronize access.
Non-thread-safe (e.g., "unsafe") Code Contexts
As a means to address these concerns, I propose that we limit read/write access to shared data to code running inside of a clearly labeled "unsafe" context, such as an
unsafe {}
block:How is
unsafe
a "guardrail"?The
unsafe
keyword is a clear signal of intent that a developer is choosing to work with shared memory multithreaded code. The presence of anunsafe
block is an indication to code reviewers that special care must be taken during review. It also is acts as a syntactic marker that future tooling (linters, type checkers, etc.) could use to identify data races.The
unsafe {}
block and Block SemanticsAn
unsafe {}
block is otherwise treated the same as a normal Block. Its only distinction is that it explicitly labels code within the block as potentially containing non-thread-safe (e.g., "unsafe") code. The general expectation is that any thread safety concerns should be addressed by the developer as control flow exits theunsafe
block. For example, you could utilizeusing
to synchronize access to a shared struct via a lock:Here, when the control enters the
unsafe
block, we allocate a lock against the provided mutex via ausing
declaration. As control exits theunsafe
block, the lock tracked byusing
is released.What does
unsafe
do?The
unsafe
keyword is a syntactic marker that applies to lexically scoped reads and writes of the fields of a shared struct instance. Within anunsafe
block, any lexically scoped accesses are permitted, even if they are nested within another function declared in the same block. This special lexical context shares some surface level similarities with the lexical scoping rules for private names, or the runtime semantics of"use strict"
.Since
unsafe
is lexically scoped, it does not carry over to the invocation of functions declared outside of anunsafe
context:Calling into, and out of,
unsafe
codeThread-safe code may execute
unsafe
code without restriction, andunsafe
code may do likewise. Asunsafe
already indicates a transition boundary between thread-safe and unsafe code, there is no need to declare all calling codeunsafe
as you might need to do forasync
/await
. Theunsafe
keyword itself does not entail any implicit synchronization or coordination as that would be in opposition to our performance goals. Instead, the onus is on developers to be cognizant of thread safety concerns when they define anunsafe
block. As such, a developer can choose the coordination mechanism that best suits the needs of their application, be that aMutex
, a lock-free concurrent deque, etc.Other
unsafe
ContextsIt should be noted that this proposal is not limited to merely
unsafe {}
, but also proposes the addition ofunsafe
as a contextual keyword in a number of other contexts so as to improve the developer experience. As such I also propose the following as possibleunsafe
contexts:unsafe {}
(as discussed above)unsafe function f() {}
(orfunction f() unsafe {}
, etc.)unsafe () => { }
unsafe async function f() {}
,unsafe function* g() {}
, etc.unsafe m() {}
unsafe get value() { ... }
unsafe constructor() {}
(though ashared struct
constructor might implicitly beunsafe
)unsafe x = ...;
static
blocks —unsafe static { }
class
/struct
/shared
struct bodies —unsafe shared struct S { ... }
unsafe const x = ...
(to avoid needing anunsafe
IIFE, splitting into alet
, etc.)There may also be other contexts we may wish to consider in the future as well.
Examples
Atomic values
Worker Coordination
The text was updated successfully, but these errors were encountered: