Skip to content

Commit

Permalink
readd golang notes
Browse files Browse the repository at this point in the history
  • Loading branch information
dingxiong committed Sep 19, 2024
1 parent 639a0d2 commit 0f328c9
Showing 1 changed file with 100 additions and 0 deletions.
100 changes: 100 additions & 0 deletions _posts/2024-04-19-golang-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,103 @@ int main(int argc, char** argv) {
return 0;
}
```
## Channel
2. What is the best practice to return a goroutine for caller to consume?
3. what does closechannel do?
Receive operator
```
x := <-ch
x, ok := <-ch
```
The implementation is
[here](https://github.com/golang/go/blob/7ba074fe43a3c1e9a35cd579520d7184d3a20d36/src/runtime/chan.go#L505)
Use space usage has `block=True`, so ignore all `!block` branches. The function
works in this sequence:
1. First, check whether the channel is nil. If it is, then this coroutine
blocks forever. You can tell from enum `traceBlockForever`.
2. Then it check whether the channel is closed or not. Note `c.closed != 0`
means channel is closed. When `c.closed != 0 && c.qcount == 0` namely,
channel is closed and nothing in the buffer, it mem-resets the content of
receiver return by `typedmemclr` and return `received = Fasle`. One the
other hand if `c.close == 0 && c.sendq is not empty`, namely, channel is
active and there is also a sender waiting to enqueue an item to this
channel, it wakes up that sender and get the item. Note, it still maintains
the order. Check the details of function `recv`.
3. After step 2, there are only two cases: (1) channel is closed and its buffer
has data. (2) the channel is open and no sender is waiting. So the rest of
this function will try to get item from channel buffer or block the channel
to wait for new sender.
In summary, `ok` will be false only when the channel is closed and empty, and
in this case, `x` is a zero value.
```
for c := range ch {}
```
How is this `for range` code compiled? The parser code is
[here](https://github.com/golang/go/blob/7ba074fe43a3c1e9a35cd579520d7184d3a20d36/src/cmd/compile/internal/walk/range.go#L265).
I pasted this code block to chatgpt and got a very satisfactory explanation.
Let's paste the code below since it is short. I removed all original comments.
```golang
case k == types.TCHAN:
ha := a
// create temp variable hv1 with type of the channel element.
hv1 := typecheck.TempAt(base.Pos, ir.CurFunc, t.Elem())
hv1.SetTypecheck(1)
if t.Elem().HasPointers() {
init = append(init, ir.NewAssignStmt(base.Pos, hv1, nil))
}
// create a bool temp variable hb.
hb := typecheck.TempAt(base.Pos, ir.CurFunc, types.Types[types.TBOOL])
// set the condition of this for loop to `hb != false`
nfor.Cond = ir.NewBinaryExpr(base.Pos, ir.ONE, hb, ir.NewBool(base.Pos, false))
lhs := []ir.Node{hv1, hb}
// rhs is expression `<-ch`
rhs := []ir.Node{ir.NewUnaryExpr(base.Pos, ir.ORECV, ha)}
// OAS2RECV:
// - O: operator
// - AS2: Assignment with 2 results
// - RECV: Receive meaning channel receive
//
// hv1, hb = <-ch
a := ir.NewAssignListStmt(base.Pos, ir.OAS2RECV, lhs, rhs)
a.SetTypecheck(1)
// Set above expression as the initializer of this for loop.
nfor.Cond = ir.InitExpr([]ir.Node{a}, nfor.Cond)
if v1 == nil {
body = nil
} else {
body = []ir.Node{rangeAssign(nrange, hv1)}
}
// set hv1 = nil to make it GC-ed as early as possible.
body = append(body, ir.NewAssignStmt(base.Pos, hv1, nil))
```

Basically, the parser tranform `for c := range ch {}` to a regular loop

```
var h1v xx;
var hb bool;
for h1v, hb = <-ch; hb != false; {
...
h1v = null
}
```

As stated in the Receive Operator section, this loop only ends when the channel
is closed and empty.

0 comments on commit 0f328c9

Please sign in to comment.