diff options
author | Ayke van Laethem <[email protected]> | 2024-11-07 13:38:28 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2024-12-01 11:12:00 +0100 |
commit | 26c36d0a2e11ba6b31300881b8dd729b1ad8cfe6 (patch) | |
tree | c65ca380fe496864f5be593bebed68cc51bf7c94 | |
parent | 09a22ac4b48b5a008cedc527c70cf41117a6b338 (diff) | |
download | tinygo-26c36d0a2e11ba6b31300881b8dd729b1ad8cfe6.tar.gz tinygo-26c36d0a2e11ba6b31300881b8dd729b1ad8cfe6.zip |
runtime: lock output in print/println
This ensures that calls to print/println happening in different threads
are not interleaved. It's a task.PMutex, so this should only change
things when threading is used.
This matches the Go compiler, which does the same thing:
https://godbolt.org/z/na5KzE7en
The locks are not recursive, which means that we need to be careful to
not call `print` or `println` inside a runtime.print* implementation,
inside putchar (recursively), and inside signal handlers. Making them
recursive might be useful to do in the future, but it's not really
necessary.
-rw-r--r-- | compiler/compiler.go | 2 | ||||
-rw-r--r-- | compiler/testdata/defer-cortex-m-qemu.ll | 10 | ||||
-rw-r--r-- | compiler/testdata/goroutine-cortex-m-qemu-tasks.ll | 6 | ||||
-rw-r--r-- | compiler/testdata/goroutine-wasm-asyncify.ll | 6 | ||||
-rw-r--r-- | src/runtime/print.go | 78 | ||||
-rw-r--r-- | testdata/print.go | 6 | ||||
-rw-r--r-- | testdata/print.txt | 6 |
7 files changed, 90 insertions, 24 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index 8b5f0d1cb..28ec312dd 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -1686,6 +1686,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c b.createRuntimeInvoke("_panic", argValues, "") return llvm.Value{}, nil case "print", "println": + b.createRuntimeCall("printlock", nil, "") for i, value := range argValues { if i >= 1 && callName == "println" { b.createRuntimeCall("printspace", nil, "") @@ -1746,6 +1747,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c if callName == "println" { b.createRuntimeCall("printnl", nil, "") } + b.createRuntimeCall("printunlock", nil, "") return llvm.Value{}, nil // print() or println() returns void case "real": cplx := argValues[0] diff --git a/compiler/testdata/defer-cortex-m-qemu.ll b/compiler/testdata/defer-cortex-m-qemu.ll index cdcfd0ea4..f1fbad7f8 100644 --- a/compiler/testdata/defer-cortex-m-qemu.ll +++ b/compiler/testdata/defer-cortex-m-qemu.ll @@ -122,12 +122,18 @@ declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2 ; Function Attrs: nounwind define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 { entry: + call void @runtime.printlock(ptr undef) #4 call void @runtime.printint32(i32 3, ptr undef) #4 + call void @runtime.printunlock(ptr undef) #4 ret void } +declare void @runtime.printlock(ptr) #2 + declare void @runtime.printint32(i32, ptr) #2 +declare void @runtime.printunlock(ptr) #2 + ; Function Attrs: nounwind define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 { entry: @@ -250,14 +256,18 @@ rundefers.end7: ; preds = %rundefers.loophead1 ; Function Attrs: nounwind define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 { entry: + call void @runtime.printlock(ptr undef) #4 call void @runtime.printint32(i32 3, ptr undef) #4 + call void @runtime.printunlock(ptr undef) #4 ret void } ; Function Attrs: nounwind define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 { entry: + call void @runtime.printlock(ptr undef) #4 call void @runtime.printint32(i32 5, ptr undef) #4 + call void @runtime.printunlock(ptr undef) #4 ret void } diff --git a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll index a57bb20f3..819f01adb 100644 --- a/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll +++ b/compiler/testdata/goroutine-cortex-m-qemu-tasks.ll @@ -70,7 +70,9 @@ entry: %stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9 %2 = load i32, ptr %n, align 4 + call void @runtime.printlock(ptr undef) #9 call void @runtime.printint32(i32 %2, ptr undef) #9 + call void @runtime.printunlock(ptr undef) #9 ret void } @@ -91,8 +93,12 @@ entry: ret void } +declare void @runtime.printlock(ptr) #2 + declare void @runtime.printint32(i32, ptr) #2 +declare void @runtime.printunlock(ptr) #2 + ; Function Attrs: nounwind define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 { entry: diff --git a/compiler/testdata/goroutine-wasm-asyncify.ll b/compiler/testdata/goroutine-wasm-asyncify.ll index c4af76037..87c7381f2 100644 --- a/compiler/testdata/goroutine-wasm-asyncify.ll +++ b/compiler/testdata/goroutine-wasm-asyncify.ll @@ -76,7 +76,9 @@ entry: store ptr %n, ptr %1, align 4 call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9 %2 = load i32, ptr %n, align 4 + call void @runtime.printlock(ptr undef) #9 call void @runtime.printint32(i32 %2, ptr undef) #9 + call void @runtime.printunlock(ptr undef) #9 ret void } @@ -98,8 +100,12 @@ entry: unreachable } +declare void @runtime.printlock(ptr) #1 + declare void @runtime.printint32(i32, ptr) #1 +declare void @runtime.printunlock(ptr) #1 + ; Function Attrs: nounwind define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 { entry: diff --git a/src/runtime/print.go b/src/runtime/print.go index ef9117ff3..a4de46025 100644 --- a/src/runtime/print.go +++ b/src/runtime/print.go @@ -1,6 +1,7 @@ package runtime import ( + "internal/task" "unsafe" ) @@ -8,6 +9,18 @@ type stringer interface { String() string } +// Lock to make sure print calls do not interleave. +// This is a no-op lock on systems that do not have parallelism. +var printLock task.PMutex + +func printlock() { + printLock.Lock() +} + +func printunlock() { + printLock.Unlock() +} + //go:nobounds func printstring(s string) { for i := 0; i < len(s); i++ { @@ -293,67 +306,84 @@ func printnl() { func printitf(msg interface{}) { switch msg := msg.(type) { case bool: - print(msg) + printbool(msg) case int: - print(msg) + switch unsafe.Sizeof(msg) { + case 8: + printint64(int64(msg)) + case 4: + printint32(int32(msg)) + } case int8: - print(msg) + printint8(msg) case int16: - print(msg) + printint16(msg) case int32: - print(msg) + printint32(msg) case int64: - print(msg) + printint64(msg) case uint: - print(msg) + switch unsafe.Sizeof(msg) { + case 8: + printuint64(uint64(msg)) + case 4: + printuint32(uint32(msg)) + } case uint8: - print(msg) + printuint8(msg) case uint16: - print(msg) + printuint16(msg) case uint32: - print(msg) + printuint32(msg) case uint64: - print(msg) + printuint64(msg) case uintptr: - print(msg) + printuintptr(msg) case float32: - print(msg) + printfloat32(msg) case float64: - print(msg) + printfloat64(msg) case complex64: - print(msg) + printcomplex64(msg) case complex128: - print(msg) + printcomplex128(msg) case string: - print(msg) + printstring(msg) case error: - print(msg.Error()) + printstring(msg.Error()) case stringer: - print(msg.String()) + printstring(msg.String()) default: // cast to underlying type itf := *(*_interface)(unsafe.Pointer(&msg)) putchar('(') printuintptr(uintptr(itf.typecode)) putchar(':') - print(itf.value) + printptr(uintptr(itf.value)) putchar(')') } } func printmap(m *hashmap) { - print("map[") + printstring("map[") if m == nil { - print("nil") + printstring("nil") } else { - print(uint(m.count)) + switch unsafe.Sizeof(m.count) { + case 8: + printuint64(uint64(m.count)) + case 4: + printuint32(uint32(m.count)) + case 2: + printuint16(uint16(m.count)) + } } putchar(']') } func printptr(ptr uintptr) { if ptr == 0 { - print("nil") + printstring("nil") return } putchar('0') diff --git a/testdata/print.go b/testdata/print.go index 7f7f843c4..5156ad58e 100644 --- a/testdata/print.go +++ b/testdata/print.go @@ -37,6 +37,12 @@ func main() { // print interface println(interface{}(nil)) + println(interface{}(true)) + println(interface{}("foobar")) + println(interface{}(int64(-3))) + println(interface{}(uint64(3))) + println(interface{}(int(-3))) + println(interface{}(uint(3))) // print map println(map[string]int{"three": 3, "five": 5}) diff --git a/testdata/print.txt b/testdata/print.txt index 116de945d..3a88cf91e 100644 --- a/testdata/print.txt +++ b/testdata/print.txt @@ -19,6 +19,12 @@ a b c +3.140000e+000 (+5.000000e+000+1.234500e+000i) (0:nil) +true +foobar +-3 +3 +-3 +3 map[2] true false [0/0]nil |