You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Abstract
Single-threaded Go code (without C or Unsafe) cannot describe memory-clobbering errors like stack smashing, map simultaneous-write, or unmutexed half-writes. Rust is similar here, but Go has better readability. During multi-threading, GoRoutines and CSP provide better ergonomics again. But multi-threaded Go brings risks of dangerous, hard-to-find memory problems. How can we change Go to alleviate these risks while retaining readability?
I propose a backard-compatible compilation switch (--strict) to bring a tough definition to complex codebases that need it.
Changes from regular Go when compiling with --strict (StrictMode-v1):
No passing pointers (or anything with references) to the creation of GoRoutines Ex: go func(copied1, copied2 any)
v2: Unless it's definitely a move: parent is owner & does not use it afterward
No modifying globals (or anything they reference) outside of init’s 1 goroutine
Any goroutines made from init must follow “runtime rules”, ex: not modify globals either.
This includes references to globals, so these will all need notation as readonly.
No passing pointers (or anything with references) over channels or panics.
Channels can be passed
v2: unless it’s a move
No sync & atomic packages(because they’re useless given the above rules) except atomic.Value is allowed for types without references.
v2: identify which variables can only be accessed when their mutex is locked
WARN on all unsafe/cgo calls unless commented as known evaluated for this main binary's package.
Obscure for -buildmode=plugin: late-init packages can not modify any globals but their own
This could be generalized to “all init cannot modify other packages globals” or strict mode could simply not allow Go plugins for v1 (neither as a build target nor allowing opening a plug-in).
How compatible is this with Today’s Go?
ALL single-thread libraries without editable globals already follow this.
Code with dependency injection is friendly to this
Item 2 (above) will need considerable clean-up in most codebases.
Items 3-5 (above) limit multithreaded communication to copies over CSP.
What’s StrictMode-v2:
The same guarantees with increased flexibility in exchange for more code analysis time.
V2 can also benefit from a runtime constant (read only) stamp. Anything constant (including constant containers full of constants) could be shared even if pointed to. This would create the same “freedom” that globals-from-init get.
Integration tests are likely the only place --strict will be used as the program should operate the same in either context. So why not put it in a linter? Because it's likely that future iterations will need new syntax for proving.
Partial Example: A thread-shared (ex) string-to-int Cache in V1:
In some cache.New: go func(ch chan<- cacheReq ) will initialize a map[string]int for the data
type cacheReq struct { Query string, retVal <-chan }
func (c *Cache)Read(key string) int {
out := make(chan string)
c.ch <- cacheReq{ Query: key, out }
return <-out
}
Cache writer not shown here for simplicity, but it will be an addition to cacheReq.
V1 will not be friendly enough for high-performance code. It is not even compatible with the http server, but it could be by V2.
Why this fix? Readability: Go would not lose its readable nature, so it would enable more junior high performance developers while keeping a stable codebase. Incremental: Just like how Escape Analysis improved, over time the restrictions will lighten and rich error messages can describe exactly why something cannot be proven safe. In the future, new optional syntax may arrive to signal intent and keep the proving work minimal. Easy First Step: Restricting syntax is fairly straightforward. Types are known at compile-time. Passing-through a readonly type-modifier (for globals) will be the hardest part of v1. Where it's too difficult to do statically, it's reasonable in v1 to throw a compile error.
The text was updated successfully, but these errors were encountered:
This is an interesting idea but it's not an actionable proposal. And it describes a language that is significant different from Go as it exists today. We don't use the issue tracker for discussions, so I'm going to close this issue. I encourage you to take it to a forum like golang-nuts. See https://go.dev/wiki/Questions.
Abstract
Single-threaded Go code (without C or Unsafe) cannot describe memory-clobbering errors like stack smashing, map simultaneous-write, or unmutexed half-writes. Rust is similar here, but Go has better readability. During multi-threading, GoRoutines and CSP provide better ergonomics again. But multi-threaded Go brings risks of dangerous, hard-to-find memory problems. How can we change Go to alleviate these risks while retaining readability?
I propose a backard-compatible compilation switch (--strict) to bring a tough definition to complex codebases that need it.
Changes from regular Go when compiling with --strict (StrictMode-v1):
How compatible is this with Today’s Go?
What’s StrictMode-v2:
The same guarantees with increased flexibility in exchange for more code analysis time.
V2 can also benefit from a runtime constant (read only) stamp. Anything constant (including constant containers full of constants) could be shared even if pointed to. This would create the same “freedom” that globals-from-init get.
Integration tests are likely the only place --strict will be used as the program should operate the same in either context. So why not put it in a linter? Because it's likely that future iterations will need new syntax for proving.
Partial Example: A thread-shared (ex) string-to-int Cache in V1:
In some cache.New: go func(ch chan<- cacheReq ) will initialize a map[string]int for the data
Cache writer not shown here for simplicity, but it will be an addition to cacheReq.
V1 will not be friendly enough for high-performance code. It is not even compatible with the http server, but it could be by V2.
Why this fix?
Readability: Go would not lose its readable nature, so it would enable more junior high performance developers while keeping a stable codebase.
Incremental: Just like how Escape Analysis improved, over time the restrictions will lighten and rich error messages can describe exactly why something cannot be proven safe. In the future, new optional syntax may arrive to signal intent and keep the proving work minimal.
Easy First Step: Restricting syntax is fairly straightforward. Types are known at compile-time. Passing-through a readonly type-modifier (for globals) will be the hardest part of v1. Where it's too difficult to do statically, it's reasonable in v1 to throw a compile error.
The text was updated successfully, but these errors were encountered: