go-runtime之chan*.go
文件注释
1 |
// Invariants: |
翻译一下:
定式:
在无缓冲通道,除非send和rece都在单线程下阻塞, c.sendq和c.recvq至少有一个是空的,而且c.sendq和c.recvq的长度只受select语法限制
hchanSize
1 |
hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1)) |
看结果是结构体hchan的实际大小,因为hchan的对齐值是8
func makechan(t chantype, size int) hchan
-
在
makechan64(t *chantype, size int64) *hchan中,size不能越界1
2
3
4
5
6
7func makechan64(t *chantype, size int64) *hchan {
if int64(int(size)) != size {
panic(plainError("makechan: size out of range"))
}
return makechan(t, int(size))
} -
元素类型大小不能超过64k
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60func makechan(t *chantype, size int) *hchan {
elem := t.elem
// compiler checks this but be safe.
// 意思大概是编译需要,实际上没有哪个的类型的大小会超过64k
// 对应在sendDirect/recvDirect的typeBitsBulkBarrier里面的64k限制
if elem.size >= 1<<16 {
throw("makechan: invalid channel element type")
}
// 基本上就是说elem的对齐了量不能超过
if hchanSize%maxAlign != 0 || elem.align > maxAlign {
throw("makechan: bad alignment")
}
// 1.size不能小于0
// 2.go内存内配限制,不允许分配大小超过限制
// 3.和2差不多意思
if size < 0 || uintptr(size) > maxSliceCap(elem.size) || uintptr(size)*elem.size > maxAlloc-hchanSize {
panic(plainError("makechan: size out of range"))
}
// Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers.
// buf points into the same allocation, elemtype is persistent.
// SudoG's are referenced from their owning thread so they can't be collected.
// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.
// Hchan存储元素与元素是否是指针值有关,具体体现在下面
var c *hchan
switch {
case size == 0 || elem.size == 0:
// 大概这就是无缓冲通道
c = (*hchan)(mallocgc(hchanSize, nil, true))
// Race detector uses this location for synchronization.
c.buf = unsafe.Pointer(c)
case elem.kind&kindNoPointers != 0:
// Elements do not contain pointers.
// Allocate hchan and buf in one call.
// 如果是无指针类型,buf和hchan是一起分配的
c = (*hchan)(mallocgc(hchanSize+uintptr(size)*elem.size, nil, true))
c.buf = add(unsafe.Pointer(c), hchanSize)
default:
// Elements contain pointers.
// 这里hchan和buf是分开分配的
c = new(hchan)
c.buf = mallocgc(uintptr(size)*elem.size, elem, true)
}
c.elemsize = uint16(elem.size)
c.elemtype = elem
c.dataqsiz = uint(size)
if debugChan {
print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n")
}
return c
}
type chantype struct {
typ _type
elem *_type
dir uintptr
}
func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool
1 |
/* |
- closechan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72func closechan(c *hchan) {
if c == nil {
panic(plainError("close of nil channel"))
}
lock(&c.lock)
if c.closed != 0 {
unlock(&c.lock)
panic(plainError("close of closed channel"))
}
if raceenabled {
callerpc := getcallerpc()
racewritepc(unsafe.Pointer(c), callerpc, funcPC(closechan))
racerelease(unsafe.Pointer(c))
}
c.closed = 1
// 这个先读后写,按顺序去除一个g,然后把所有的g通过schedlink连接到一起
var glist *g
// release all readers
for {
sg := c.recvq.dequeue()
if sg == nil {
break
}
if sg.elem != nil {
typedmemclr(c.elemtype, sg.elem)
sg.elem = nil
}
if sg.releasetime != 0 { // 上面 releasetime = -1 的用处
sg.releasetime = cputicks()
}
gp := sg.g
gp.param = nil
if raceenabled {
raceacquireg(gp, unsafe.Pointer(c))
}
gp.schedlink.set(glist)
glist = gp
}
// release all writers (they will panic)
for {
sg := c.sendq.dequeue()
if sg == nil {
break
}
sg.elem = nil
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
gp := sg.g
gp.param = nil
if raceenabled {
raceacquireg(gp, unsafe.Pointer(c))
}
gp.schedlink.set(glist)
glist = gp
}
unlock(&c.lock)
// Ready all Gs now that we've dropped the channel lock.
for glist != nil {
gp := glist
glist = glist.schedlink.ptr()
gp.schedlink = 0
goready(gp, 3) // goreadey解除程阻塞状态
}
}
chanrecv
1 |
|
chanrecv和chansend的block为false的情况
1 |
func TestChan6(t *testing.T) { |
default或其他条件的存在使得ch不是block读
补充:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 // compiler implements
//
// select {
// case c <- v:
// ... foo
// default:
// ... bar
// }
//
// as
//
// if selectnbsend(c, v) {
// ... foo
// } else {
// ... bar
// }
//
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
return chansend(c, elem, false, getcallerpc())
}
虽然文档说这样子是非阻塞调用,但是在debug的时候并没有见到该函数被调用!!