Skip to content

Commit 26c36d0

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 09a22ac commit 26c36d0

File tree

7 files changed

+90
-24
lines changed

7 files changed

+90
-24
lines changed

compiler/compiler.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
16861686
b.createRuntimeInvoke("_panic", argValues, "")
16871687
return llvm.Value{}, nil
16881688
case "print", "println":
1689+
b.createRuntimeCall("printlock", nil, "")
16891690
for i, value := range argValues {
16901691
if i >= 1 && callName == "println" {
16911692
b.createRuntimeCall("printspace", nil, "")
@@ -1746,6 +1747,7 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
17461747
if callName == "println" {
17471748
b.createRuntimeCall("printnl", nil, "")
17481749
}
1750+
b.createRuntimeCall("printunlock", nil, "")
17491751
return llvm.Value{}, nil // print() or println() returns void
17501752
case "real":
17511753
cplx := argValues[0]

compiler/testdata/defer-cortex-m-qemu.ll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,18 @@ declare void @runtime.destroyDeferFrame(ptr dereferenceable_or_null(24), ptr) #2
122122
; Function Attrs: nounwind
123123
define internal void @"main.deferSimple$1"(ptr %context) unnamed_addr #1 {
124124
entry:
125+
call void @runtime.printlock(ptr undef) #4
125126
call void @runtime.printint32(i32 3, ptr undef) #4
127+
call void @runtime.printunlock(ptr undef) #4
126128
ret void
127129
}
128130

131+
declare void @runtime.printlock(ptr) #2
132+
129133
declare void @runtime.printint32(i32, ptr) #2
130134

135+
declare void @runtime.printunlock(ptr) #2
136+
131137
; Function Attrs: nounwind
132138
define hidden void @main.deferMultiple(ptr %context) unnamed_addr #1 {
133139
entry:
@@ -250,14 +256,18 @@ rundefers.end7: ; preds = %rundefers.loophead1
250256
; Function Attrs: nounwind
251257
define internal void @"main.deferMultiple$1"(ptr %context) unnamed_addr #1 {
252258
entry:
259+
call void @runtime.printlock(ptr undef) #4
253260
call void @runtime.printint32(i32 3, ptr undef) #4
261+
call void @runtime.printunlock(ptr undef) #4
254262
ret void
255263
}
256264

257265
; Function Attrs: nounwind
258266
define internal void @"main.deferMultiple$2"(ptr %context) unnamed_addr #1 {
259267
entry:
268+
call void @runtime.printlock(ptr undef) #4
260269
call void @runtime.printint32(i32 5, ptr undef) #4
270+
call void @runtime.printunlock(ptr undef) #4
261271
ret void
262272
}
263273

compiler/testdata/goroutine-cortex-m-qemu-tasks.ll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ entry:
7070
%stacksize = call i32 @"internal/task.getGoroutineStackSize"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr undef) #9
7171
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 %stacksize, ptr undef) #9
7272
%2 = load i32, ptr %n, align 4
73+
call void @runtime.printlock(ptr undef) #9
7374
call void @runtime.printint32(i32 %2, ptr undef) #9
75+
call void @runtime.printunlock(ptr undef) #9
7476
ret void
7577
}
7678

@@ -91,8 +93,12 @@ entry:
9193
ret void
9294
}
9395

96+
declare void @runtime.printlock(ptr) #2
97+
9498
declare void @runtime.printint32(i32, ptr) #2
9599

100+
declare void @runtime.printunlock(ptr) #2
101+
96102
; Function Attrs: nounwind
97103
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #1 {
98104
entry:

compiler/testdata/goroutine-wasm-asyncify.ll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ entry:
7676
store ptr %n, ptr %1, align 4
7777
call void @"internal/task.start"(i32 ptrtoint (ptr @"main.closureFunctionGoroutine$1$gowrapper" to i32), ptr nonnull %0, i32 65536, ptr undef) #9
7878
%2 = load i32, ptr %n, align 4
79+
call void @runtime.printlock(ptr undef) #9
7980
call void @runtime.printint32(i32 %2, ptr undef) #9
81+
call void @runtime.printunlock(ptr undef) #9
8082
ret void
8183
}
8284

@@ -98,8 +100,12 @@ entry:
98100
unreachable
99101
}
100102

103+
declare void @runtime.printlock(ptr) #1
104+
101105
declare void @runtime.printint32(i32, ptr) #1
102106

107+
declare void @runtime.printunlock(ptr) #1
108+
103109
; Function Attrs: nounwind
104110
define hidden void @main.funcGoroutine(ptr %fn.context, ptr %fn.funcptr, ptr %context) unnamed_addr #2 {
105111
entry:

src/runtime/print.go

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
package runtime
22

33
import (
4+
"internal/task"
45
"unsafe"
56
)
67

78
type stringer interface {
89
String() string
910
}
1011

12+
// Lock to make sure print calls do not interleave.
13+
// This is a no-op lock on systems that do not have parallelism.
14+
var printLock task.PMutex
15+
16+
func printlock() {
17+
printLock.Lock()
18+
}
19+
20+
func printunlock() {
21+
printLock.Unlock()
22+
}
23+
1124
//go:nobounds
1225
func printstring(s string) {
1326
for i := 0; i < len(s); i++ {
@@ -293,67 +306,84 @@ func printnl() {
293306
func printitf(msg interface{}) {
294307
switch msg := msg.(type) {
295308
case bool:
296-
print(msg)
309+
printbool(msg)
297310
case int:
298-
print(msg)
311+
switch unsafe.Sizeof(msg) {
312+
case 8:
313+
printint64(int64(msg))
314+
case 4:
315+
printint32(int32(msg))
316+
}
299317
case int8:
300-
print(msg)
318+
printint8(msg)
301319
case int16:
302-
print(msg)
320+
printint16(msg)
303321
case int32:
304-
print(msg)
322+
printint32(msg)
305323
case int64:
306-
print(msg)
324+
printint64(msg)
307325
case uint:
308-
print(msg)
326+
switch unsafe.Sizeof(msg) {
327+
case 8:
328+
printuint64(uint64(msg))
329+
case 4:
330+
printuint32(uint32(msg))
331+
}
309332
case uint8:
310-
print(msg)
333+
printuint8(msg)
311334
case uint16:
312-
print(msg)
335+
printuint16(msg)
313336
case uint32:
314-
print(msg)
337+
printuint32(msg)
315338
case uint64:
316-
print(msg)
339+
printuint64(msg)
317340
case uintptr:
318-
print(msg)
341+
printuintptr(msg)
319342
case float32:
320-
print(msg)
343+
printfloat32(msg)
321344
case float64:
322-
print(msg)
345+
printfloat64(msg)
323346
case complex64:
324-
print(msg)
347+
printcomplex64(msg)
325348
case complex128:
326-
print(msg)
349+
printcomplex128(msg)
327350
case string:
328-
print(msg)
351+
printstring(msg)
329352
case error:
330-
print(msg.Error())
353+
printstring(msg.Error())
331354
case stringer:
332-
print(msg.String())
355+
printstring(msg.String())
333356
default:
334357
// cast to underlying type
335358
itf := *(*_interface)(unsafe.Pointer(&msg))
336359
putchar('(')
337360
printuintptr(uintptr(itf.typecode))
338361
putchar(':')
339-
print(itf.value)
362+
printptr(uintptr(itf.value))
340363
putchar(')')
341364
}
342365
}
343366

344367
func printmap(m *hashmap) {
345-
print("map[")
368+
printstring("map[")
346369
if m == nil {
347-
print("nil")
370+
printstring("nil")
348371
} else {
349-
print(uint(m.count))
372+
switch unsafe.Sizeof(m.count) {
373+
case 8:
374+
printuint64(uint64(m.count))
375+
case 4:
376+
printuint32(uint32(m.count))
377+
case 2:
378+
printuint16(uint16(m.count))
379+
}
350380
}
351381
putchar(']')
352382
}
353383

354384
func printptr(ptr uintptr) {
355385
if ptr == 0 {
356-
print("nil")
386+
printstring("nil")
357387
return
358388
}
359389
putchar('0')

testdata/print.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ func main() {
3737

3838
// print interface
3939
println(interface{}(nil))
40+
println(interface{}(true))
41+
println(interface{}("foobar"))
42+
println(interface{}(int64(-3)))
43+
println(interface{}(uint64(3)))
44+
println(interface{}(int(-3)))
45+
println(interface{}(uint(3)))
4046

4147
// print map
4248
println(map[string]int{"three": 3, "five": 5})

testdata/print.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ a b c
1919
+3.140000e+000
2020
(+5.000000e+000+1.234500e+000i)
2121
(0:nil)
22+
true
23+
foobar
24+
-3
25+
3
26+
-3
27+
3
2228
map[2]
2329
true false
2430
[0/0]nil

0 commit comments

Comments
 (0)