Skip to content

Commit b44c02b

Browse files
committed
runtime: make channels work in interrupts
1 parent e86ca20 commit b44c02b

File tree

13 files changed

+232
-33
lines changed

13 files changed

+232
-33
lines changed

src/examples/systick/systick.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,27 @@ import (
55
"machine"
66
)
77

8+
var timerCh = make(chan struct{}, 1)
9+
810
func main() {
911
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
1012

1113
// timer fires 10 times per second
1214
arm.SetupSystemTimer(machine.CPUFrequency() / 10)
1315

1416
for {
17+
machine.LED.Low()
18+
<-timerCh
19+
machine.LED.High()
20+
<-timerCh
1521
}
1622
}
1723

18-
var led_state bool
19-
2024
//export SysTick_Handler
2125
func timer_isr() {
22-
if led_state {
23-
machine.LED.Low()
24-
} else {
25-
machine.LED.High()
26+
select {
27+
case timerCh <- struct{}{}:
28+
default:
29+
// The consumer is running behind.
2630
}
27-
led_state = !led_state
2831
}

src/internal/task/queue.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package task
22

3+
import "runtime/interrupt"
4+
35
const asserts = false
46

57
// Queue is a FIFO container of tasks.
@@ -10,7 +12,9 @@ type Queue struct {
1012

1113
// Push a task onto the queue.
1214
func (q *Queue) Push(t *Task) {
15+
i := interrupt.Disable()
1316
if asserts && t.Next != nil {
17+
interrupt.Restore(i)
1418
panic("runtime: pushing a task to a queue with a non-nil Next pointer")
1519
}
1620
if q.tail != nil {
@@ -21,31 +25,45 @@ func (q *Queue) Push(t *Task) {
2125
if q.head == nil {
2226
q.head = t
2327
}
28+
interrupt.Restore(i)
2429
}
2530

2631
// Pop a task off of the queue.
2732
func (q *Queue) Pop() *Task {
33+
i := interrupt.Disable()
2834
t := q.head
2935
if t == nil {
36+
interrupt.Restore(i)
3037
return nil
3138
}
3239
q.head = t.Next
3340
if q.tail == t {
3441
q.tail = nil
3542
}
3643
t.Next = nil
44+
interrupt.Restore(i)
3745
return t
3846
}
3947

4048
// Append pops the contents of another queue and pushes them onto the end of this queue.
4149
func (q *Queue) Append(other *Queue) {
50+
i := interrupt.Disable()
4251
if q.head == nil {
4352
q.head = other.head
4453
} else {
4554
q.tail.Next = other.head
4655
}
4756
q.tail = other.tail
4857
other.head, other.tail = nil, nil
58+
interrupt.Restore(i)
59+
}
60+
61+
// Empty checks if the queue is empty.
62+
func (q *Queue) Empty() bool {
63+
i := interrupt.Disable()
64+
empty := q.head == nil
65+
interrupt.Restore(i)
66+
return empty
4967
}
5068

5169
// Stack is a LIFO container of tasks.
@@ -57,19 +75,24 @@ type Stack struct {
5775

5876
// Push a task onto the stack.
5977
func (s *Stack) Push(t *Task) {
78+
i := interrupt.Disable()
6079
if asserts && t.Next != nil {
80+
interrupt.Restore(i)
6181
panic("runtime: pushing a task to a stack with a non-nil Next pointer")
6282
}
6383
s.top, t.Next = t, s.top
84+
interrupt.Restore(i)
6485
}
6586

6687
// Pop a task off of the stack.
6788
func (s *Stack) Pop() *Task {
89+
i := interrupt.Disable()
6890
t := s.top
6991
if t != nil {
7092
s.top = t.Next
7193
t.Next = nil
7294
}
95+
interrupt.Restore(i)
7396
return t
7497
}
7598

@@ -89,10 +112,13 @@ func (t *Task) tail() *Task {
89112
// Queue moves the contents of the stack into a queue.
90113
// Elements can be popped from the queue in the same order that they would be popped from the stack.
91114
func (s *Stack) Queue() Queue {
115+
i := interrupt.Disable()
92116
head := s.top
93117
s.top = nil
94-
return Queue{
118+
q := Queue{
95119
head: head,
96120
tail: head.tail(),
97121
}
122+
interrupt.Restore(i)
123+
return q
98124
}

src/runtime/arch_cortexm.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,7 @@ func procPin() {
102102
func procUnpin() {
103103
arm.EnableInterrupts(procPinnedMask)
104104
}
105+
106+
func waitForEvents() {
107+
arm.Asm("wfe")
108+
}

src/runtime/arch_tinygoriscv.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,11 @@ func procPin() {
9090
func procUnpin() {
9191
riscv.EnableInterrupts(procPinnedMask)
9292
}
93+
94+
func waitForEvents() {
95+
mask := riscv.DisableInterrupts()
96+
if !runqueue.Empty() {
97+
riscv.Asm("wfi")
98+
}
99+
riscv.EnableInterrupts(mask)
100+
}

src/runtime/chan.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ package runtime
2525

2626
import (
2727
"internal/task"
28+
"runtime/interrupt"
2829
"unsafe"
2930
)
3031

@@ -308,13 +309,17 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
308309
return false
309310
}
310311

312+
i := interrupt.Disable()
313+
311314
switch ch.state {
312315
case chanStateEmpty, chanStateBuf:
313316
// try to dump the value directly into the buffer
314317
if ch.push(value) {
315318
ch.state = chanStateBuf
319+
interrupt.Restore(i)
316320
return true
317321
}
322+
interrupt.Restore(i)
318323
return false
319324
case chanStateRecv:
320325
// unblock reciever
@@ -328,16 +333,21 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
328333
ch.state = chanStateEmpty
329334
}
330335

336+
interrupt.Restore(i)
331337
return true
332338
case chanStateSend:
333339
// something else is already waiting to send
340+
interrupt.Restore(i)
334341
return false
335342
case chanStateClosed:
343+
interrupt.Restore(i)
336344
runtimePanic("send on closed channel")
337345
default:
346+
interrupt.Restore(i)
338347
runtimePanic("invalid channel state")
339348
}
340349

350+
interrupt.Restore(i)
341351
return false
342352
}
343353

@@ -351,6 +361,8 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
351361
return false, false
352362
}
353363

364+
i := interrupt.Disable()
365+
354366
switch ch.state {
355367
case chanStateBuf, chanStateSend:
356368
// try to pop the value directly from the buffer
@@ -373,6 +385,7 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
373385
ch.state = chanStateEmpty
374386
}
375387

388+
interrupt.Restore(i)
376389
return true, true
377390
} else if ch.blocked != nil {
378391
// unblock next sender if applicable
@@ -386,19 +399,24 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (bool, bool) {
386399
ch.state = chanStateEmpty
387400
}
388401

402+
interrupt.Restore(i)
389403
return true, true
390404
}
405+
interrupt.Restore(i)
391406
return false, false
392407
case chanStateRecv, chanStateEmpty:
393408
// something else is already waiting to recieve
409+
interrupt.Restore(i)
394410
return false, false
395411
case chanStateClosed:
396412
if ch.pop(value) {
413+
interrupt.Restore(i)
397414
return true, true
398415
}
399416

400417
// channel closed - nothing to recieve
401418
memzero(value, ch.elementSize)
419+
interrupt.Restore(i)
402420
return true, false
403421
default:
404422
runtimePanic("invalid channel state")
@@ -447,14 +465,18 @@ type chanSelectState struct {
447465
// This operation will block unless a value is immediately available.
448466
// May panic if the channel is closed.
449467
func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) {
468+
i := interrupt.Disable()
469+
450470
if ch.trySend(value) {
451471
// value immediately sent
452472
chanDebug(ch)
473+
interrupt.Restore(i)
453474
return
454475
}
455476

456477
if ch == nil {
457478
// A nil channel blocks forever. Do not schedule this goroutine again.
479+
interrupt.Restore(i)
458480
deadlock()
459481
}
460482

@@ -468,6 +490,7 @@ func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
468490
}
469491
ch.blocked = blockedlist
470492
chanDebug(ch)
493+
interrupt.Restore(i)
471494
task.Pause()
472495
sender.Ptr = nil
473496
}
@@ -477,14 +500,18 @@ func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
477500
// The recieved value is copied into the value pointer.
478501
// Returns the comma-ok value.
479502
func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) bool {
503+
i := interrupt.Disable()
504+
480505
if rx, ok := ch.tryRecv(value); rx {
481506
// value immediately available
482507
chanDebug(ch)
508+
interrupt.Restore(i)
483509
return ok
484510
}
485511

486512
if ch == nil {
487513
// A nil channel blocks forever. Do not schedule this goroutine again.
514+
interrupt.Restore(i)
488515
deadlock()
489516
}
490517

@@ -498,6 +525,7 @@ func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList
498525
}
499526
ch.blocked = blockedlist
500527
chanDebug(ch)
528+
interrupt.Restore(i)
501529
task.Pause()
502530
ok := receiver.Data == 1
503531
receiver.Ptr, receiver.Data = nil, 0
@@ -511,15 +539,18 @@ func chanClose(ch *channel) {
511539
// Not allowed by the language spec.
512540
runtimePanic("close of nil channel")
513541
}
542+
i := interrupt.Disable()
514543
switch ch.state {
515544
case chanStateClosed:
516545
// Not allowed by the language spec.
546+
interrupt.Restore(i)
517547
runtimePanic("close of closed channel")
518548
case chanStateSend:
519549
// This panic should ideally on the sending side, not in this goroutine.
520550
// But when a goroutine tries to send while the channel is being closed,
521551
// that is clearly invalid: the send should have been completed already
522552
// before the close.
553+
interrupt.Restore(i)
523554
runtimePanic("close channel during send")
524555
case chanStateRecv:
525556
// unblock all receivers with the zero value
@@ -531,6 +562,7 @@ func chanClose(ch *channel) {
531562
// Easy case. No available sender or receiver.
532563
}
533564
ch.state = chanStateClosed
565+
interrupt.Restore(i)
534566
chanDebug(ch)
535567
}
536568

@@ -541,8 +573,11 @@ func chanClose(ch *channel) {
541573
// TODO: do this in a round-robin fashion (as specified in the Go spec) instead
542574
// of picking the first one that can proceed.
543575
func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelBlockedList) (uintptr, bool) {
576+
istate := interrupt.Disable()
577+
544578
if selected, ok := tryChanSelect(recvbuf, states); selected != ^uintptr(0) {
545579
// one channel was immediately ready
580+
interrupt.Restore(istate)
546581
return selected, ok
547582
}
548583

@@ -570,6 +605,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
570605
case chanStateRecv:
571606
// already in correct state
572607
default:
608+
interrupt.Restore(istate)
573609
runtimePanic("invalid channel state")
574610
}
575611
} else {
@@ -582,6 +618,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
582618
case chanStateBuf:
583619
// already in correct state
584620
default:
621+
interrupt.Restore(istate)
585622
runtimePanic("invalid channel state")
586623
}
587624
}
@@ -594,6 +631,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
594631
t.Data = 1
595632

596633
// wait for one case to fire
634+
interrupt.Restore(istate)
597635
task.Pause()
598636

599637
// figure out which one fired and return the ok value
@@ -602,22 +640,27 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelB
602640

603641
// tryChanSelect is like chanSelect, but it does a non-blocking select operation.
604642
func tryChanSelect(recvbuf unsafe.Pointer, states []chanSelectState) (uintptr, bool) {
643+
istate := interrupt.Disable()
644+
605645
// See whether we can receive from one of the channels.
606646
for i, state := range states {
607647
if state.value == nil {
608648
// A receive operation.
609649
if rx, ok := state.ch.tryRecv(recvbuf); rx {
610650
chanDebug(state.ch)
651+
interrupt.Restore(istate)
611652
return uintptr(i), ok
612653
}
613654
} else {
614655
// A send operation: state.value is not nil.
615656
if state.ch.trySend(state.value) {
616657
chanDebug(state.ch)
658+
interrupt.Restore(istate)
617659
return uintptr(i), true
618660
}
619661
}
620662
}
621663

664+
interrupt.Restore(istate)
622665
return ^uintptr(0), false
623666
}

0 commit comments

Comments
 (0)