From 0f328c9783603005222db0705b2d72d39aa87977 Mon Sep 17 00:00:00 2001 From: Xiong Ding Date: Wed, 18 Sep 2024 23:53:44 -0700 Subject: [PATCH] readd golang notes --- _posts/2024-04-19-golang-getting-started.md | 100 ++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/_posts/2024-04-19-golang-getting-started.md b/_posts/2024-04-19-golang-getting-started.md index e94e235..2083e36 100644 --- a/_posts/2024-04-19-golang-getting-started.md +++ b/_posts/2024-04-19-golang-getting-started.md @@ -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.