Skip to content

Conversation

@asteurer
Copy link
Contributor

@asteurer asteurer commented Jan 7, 2026

Overview

This change is intended to prevent concurrent reads and writes on streams and futures

Closes #1460

Observations

Given the following guest application code:

func Handle(request *Request) Result[*Response, ErrorCode] {
	tx, rx := MakeStreamU8()

	go func() {
		defer tx.Drop()
		for i := 0; i < 100; i++ {
			tx.Write([]byte(fmt.Sprintf("Hello %d", i)))
			time.Sleep(1 * time.Millisecond)
		}
	}()

	var wg sync.WaitGroup
	for i := 0; i < 3; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			buffer := make([]uint8, 7)
			for j := 0; j < 10; j++ {
				// ERROR: READ CONCURRENTLY
				count := rx.Read(buffer)
				if count == 0 {
					break
				}

				fmt.Printf("output: %s", string(buffer))
			}
		}(i)
	}

	wg.Wait()

	return Err[*wasi_http_handler.Response](MakeErrorCodeDestinationIpUnroutable())

}

I would expect there to be a panic and have there be a "nil handle" message; however, this is the output I get:

worker error: error while executing at wasm backtrace:
    0: 0x2916ef - <unknown>!<wasm function 16>
    1: 0x182eb0 - <unknown>!wit_component_wasi_http_types.wasm_stream_read_u8
    2: 0x184933 - <unknown>!github.com_bytecodealliance_wit_bindgen_wit_types.__StreamReader_go.shape.uint8__.Read
    3: 0x1890b5 - <unknown>!wit_component_export_wasi_http_handler.Handle.func2
    4: 0x188d81 - <unknown>!wit_component_export_wasi_http_handler.Handle.gowrap1
    5: 0x12204b - <unknown>!wasm_pc_f_loop_export
    6: 0x18c0d1 - <unknown>!_async_lift_wasi_http_handler_0.3.0_rc_2025_09_16_handle
error: channel closed

This is concerning because the Goroutine appears to be moving past the panic in the wit_runtime.Handle.Take() method:

func (self *Handle) Take() int32 {
	if self.value == 0 {
		panic("nil handle")
	}
	value := self.value
	self.value = 0
	return value
}

@dicej any thoughts and/or suggestions?

@dicej
Copy link
Collaborator

dicej commented Jan 7, 2026

time.Sleep(1 * time.Millisecond)

time.Sleep will block the entire runtime since there's no GOOS=wasip3 yet. You'll need to use wasi:[email protected] 2025-09-26/monotonic-clock#wait-for instead (i.e. generating bindings from the WASIp3 WIT files and using those directly).

@asteurer asteurer requested a review from dicej January 7, 2026 22:38
@asteurer
Copy link
Contributor Author

asteurer commented Jan 7, 2026

@dicej Looks like the CI/Check GH Actions workflow failed. I assume the fix for that is just re-running it? If so, would you mind doing that for me?

@dicej
Copy link
Collaborator

dicej commented Jan 7, 2026

@dicej Looks like the CI/Check GH Actions workflow failed. I assume the fix for that is just re-running it? If so, would you mind doing that for me?

👍

BTW, would you be able to add test coverage for this to the tests/runtime-async suite, perhaps by expanding simple-stream-payload?

@asteurer
Copy link
Contributor Author

asteurer commented Jan 8, 2026

@dicej When you have a moment, this is ready to be reviewed

Copy link
Collaborator

@dicej dicej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks! Just a few doc tweak suggestions.

Signed-off-by: Andrew Steurer <[email protected]>

fix: adding panic for empty dst slice, removing handle reassignment for futures

Signed-off-by: Andrew Steurer <[email protected]>

docs(go): adding package comments

Signed-off-by: Andrew Steurer <[email protected]>

feat(go): add future read concurrency test

Signed-off-by: Andrew Steurer <[email protected]>

feat(go): adding remaining concurrent read/write tests

Signed-off-by: Andrew Steurer <[email protected]>

doc(go): adding comments on drop methods

Signed-off-by: Andrew Steurer <[email protected]>

Update crates/go/src/package/wit_types/wit_stream.go

Co-authored-by: Joel Dice <[email protected]>

Update crates/go/src/package/wit_types/wit_future.go

Co-authored-by: Joel Dice <[email protected]>

Update crates/go/src/package/wit_types/wit_future.go

Co-authored-by: Joel Dice <[email protected]>
@asteurer asteurer requested a review from dicej January 8, 2026 23:02
@dicej dicej enabled auto-merge January 8, 2026 23:03
@dicej dicej added this pull request to the merge queue Jan 8, 2026
Merged via the queue into bytecodealliance:main with commit 44225ca Jan 8, 2026
27 checks passed
@asteurer asteurer deleted the concurrent branch January 8, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

go: prevent concurrent reads or writes on the same stream or future

2 participants