github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/runtime/chan.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 // This file contains the implementation of Go channels. 8 9 import "unsafe" 10 11 const ( 12 maxAlign = 8 13 hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1)) 14 debugChan = false 15 ) 16 17 type hchan struct { 18 qcount uint // total data in the queue 19 dataqsiz uint // size of the circular queue 20 buf unsafe.Pointer // points to an array of dataqsiz elements 21 elemsize uint16 22 closed uint32 23 elemtype *_type // element type 24 sendx uint // send index 25 recvx uint // receive index 26 recvq waitq // list of recv waiters 27 sendq waitq // list of send waiters 28 lock mutex 29 } 30 31 type waitq struct { 32 first *sudog 33 last *sudog 34 } 35 36 //go:linkname reflect_makechan reflect.makechan 37 func reflect_makechan(t *chantype, size int64) *hchan { 38 return makechan(t, size) 39 } 40 41 func makechan(t *chantype, size int64) *hchan { 42 elem := t.elem 43 44 // compiler checks this but be safe. 45 if elem.size >= 1<<16 { 46 throw("makechan: invalid channel element type") 47 } 48 if hchanSize%maxAlign != 0 || elem.align > maxAlign { 49 throw("makechan: bad alignment") 50 } 51 if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/uintptr(elem.size)) { 52 panic("makechan: size out of range") 53 } 54 55 var c *hchan 56 if elem.kind&kindNoPointers != 0 || size == 0 { 57 // Allocate memory in one call. 58 // Hchan does not contain pointers interesting for GC in this case: 59 // buf points into the same allocation, elemtype is persistent. 60 // SudoG's are referenced from their owning thread so they can't be collected. 61 // TODO(dvyukov,rlh): Rethink when collector can move allocated objects. 62 c = (*hchan)(mallocgc(hchanSize+uintptr(size)*uintptr(elem.size), nil, flagNoScan)) 63 if size > 0 && elem.size != 0 { 64 c.buf = add(unsafe.Pointer(c), hchanSize) 65 } else { 66 // race detector uses this location for synchronization 67 // Also prevents us from pointing beyond the allocation (see issue 9401). 68 c.buf = unsafe.Pointer(c) 69 } 70 } else { 71 c = new(hchan) 72 c.buf = newarray(elem, uintptr(size)) 73 } 74 c.elemsize = uint16(elem.size) 75 c.elemtype = elem 76 c.dataqsiz = uint(size) 77 78 if debugChan { 79 print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n") 80 } 81 return c 82 } 83 84 // chanbuf(c, i) is pointer to the i'th slot in the buffer. 85 func chanbuf(c *hchan, i uint) unsafe.Pointer { 86 return add(c.buf, uintptr(i)*uintptr(c.elemsize)) 87 } 88 89 // entry point for c <- x from compiled code 90 //go:nosplit 91 func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) { 92 chansend(t, c, elem, true, getcallerpc(unsafe.Pointer(&t))) 93 } 94 95 /* 96 * generic single channel send/recv 97 * If block is not nil, 98 * then the protocol will not 99 * sleep but return if it could 100 * not complete. 101 * 102 * sleep can wake up with g.param == nil 103 * when a channel involved in the sleep has 104 * been closed. it is easiest to loop and re-run 105 * the operation; we'll see that it's now closed. 106 */ 107 func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { 108 if raceenabled { 109 raceReadObjectPC(t.elem, ep, callerpc, funcPC(chansend)) 110 } 111 if msanenabled { 112 msanread(ep, t.elem.size) 113 } 114 115 if c == nil { 116 if !block { 117 return false 118 } 119 gopark(nil, nil, "chan send (nil chan)", traceEvGoStop, 2) 120 throw("unreachable") 121 } 122 123 if debugChan { 124 print("chansend: chan=", c, "\n") 125 } 126 127 if raceenabled { 128 racereadpc(unsafe.Pointer(c), callerpc, funcPC(chansend)) 129 } 130 131 // Fast path: check for failed non-blocking operation without acquiring the lock. 132 // 133 // After observing that the channel is not closed, we observe that the channel is 134 // not ready for sending. Each of these observations is a single word-sized read 135 // (first c.closed and second c.recvq.first or c.qcount depending on kind of channel). 136 // Because a closed channel cannot transition from 'ready for sending' to 137 // 'not ready for sending', even if the channel is closed between the two observations, 138 // they imply a moment between the two when the channel was both not yet closed 139 // and not ready for sending. We behave as if we observed the channel at that moment, 140 // and report that the send cannot proceed. 141 // 142 // It is okay if the reads are reordered here: if we observe that the channel is not 143 // ready for sending and then observe that it is not closed, that implies that the 144 // channel wasn't closed during the first observation. 145 if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) || 146 (c.dataqsiz > 0 && c.qcount == c.dataqsiz)) { 147 return false 148 } 149 150 var t0 int64 151 if blockprofilerate > 0 { 152 t0 = cputicks() 153 } 154 155 lock(&c.lock) 156 if c.closed != 0 { 157 unlock(&c.lock) 158 panic("send on closed channel") 159 } 160 161 if c.dataqsiz == 0 { // synchronous channel 162 sg := c.recvq.dequeue() 163 if sg != nil { // found a waiting receiver 164 if raceenabled { 165 racesync(c, sg) 166 } 167 unlock(&c.lock) 168 169 recvg := sg.g 170 if sg.elem != nil { 171 syncsend(c, sg, ep) 172 } 173 recvg.param = unsafe.Pointer(sg) 174 if sg.releasetime != 0 { 175 sg.releasetime = cputicks() 176 } 177 goready(recvg, 3) 178 return true 179 } 180 181 if !block { 182 unlock(&c.lock) 183 return false 184 } 185 186 // no receiver available: block on this channel. 187 gp := getg() 188 mysg := acquireSudog() 189 mysg.releasetime = 0 190 if t0 != 0 { 191 mysg.releasetime = -1 192 } 193 mysg.elem = ep 194 mysg.waitlink = nil 195 gp.waiting = mysg 196 mysg.g = gp 197 mysg.selectdone = nil 198 gp.param = nil 199 c.sendq.enqueue(mysg) 200 goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3) 201 202 // someone woke us up. 203 if mysg != gp.waiting { 204 throw("G waiting list is corrupted!") 205 } 206 gp.waiting = nil 207 if gp.param == nil { 208 if c.closed == 0 { 209 throw("chansend: spurious wakeup") 210 } 211 panic("send on closed channel") 212 } 213 gp.param = nil 214 if mysg.releasetime > 0 { 215 blockevent(int64(mysg.releasetime)-t0, 2) 216 } 217 releaseSudog(mysg) 218 return true 219 } 220 221 // asynchronous channel 222 // wait for some space to write our data 223 var t1 int64 224 for futile := byte(0); c.qcount >= c.dataqsiz; futile = traceFutileWakeup { 225 if !block { 226 unlock(&c.lock) 227 return false 228 } 229 gp := getg() 230 mysg := acquireSudog() 231 mysg.releasetime = 0 232 if t0 != 0 { 233 mysg.releasetime = -1 234 } 235 mysg.g = gp 236 mysg.elem = nil 237 mysg.selectdone = nil 238 c.sendq.enqueue(mysg) 239 goparkunlock(&c.lock, "chan send", traceEvGoBlockSend|futile, 3) 240 241 // someone woke us up - try again 242 if mysg.releasetime > 0 { 243 t1 = mysg.releasetime 244 } 245 releaseSudog(mysg) 246 lock(&c.lock) 247 if c.closed != 0 { 248 unlock(&c.lock) 249 panic("send on closed channel") 250 } 251 } 252 253 // write our data into the channel buffer 254 if raceenabled { 255 raceacquire(chanbuf(c, c.sendx)) 256 racerelease(chanbuf(c, c.sendx)) 257 } 258 typedmemmove(c.elemtype, chanbuf(c, c.sendx), ep) 259 c.sendx++ 260 if c.sendx == c.dataqsiz { 261 c.sendx = 0 262 } 263 c.qcount++ 264 265 // wake up a waiting receiver 266 sg := c.recvq.dequeue() 267 if sg != nil { 268 recvg := sg.g 269 unlock(&c.lock) 270 if sg.releasetime != 0 { 271 sg.releasetime = cputicks() 272 } 273 goready(recvg, 3) 274 } else { 275 unlock(&c.lock) 276 } 277 if t1 > 0 { 278 blockevent(t1-t0, 2) 279 } 280 return true 281 } 282 283 func syncsend(c *hchan, sg *sudog, elem unsafe.Pointer) { 284 // Send on unbuffered channel is the only operation 285 // in the entire runtime where one goroutine 286 // writes to the stack of another goroutine. The GC assumes that 287 // stack writes only happen when the goroutine is running and are 288 // only done by that goroutine. Using a write barrier is sufficient to 289 // make up for violating that assumption, but the write barrier has to work. 290 // typedmemmove will call heapBitsBulkBarrier, but the target bytes 291 // are not in the heap, so that will not help. We arrange to call 292 // memmove and typeBitsBulkBarrier instead. 293 memmove(sg.elem, elem, c.elemtype.size) 294 typeBitsBulkBarrier(c.elemtype, uintptr(sg.elem), c.elemtype.size) 295 sg.elem = nil 296 } 297 298 func closechan(c *hchan) { 299 if c == nil { 300 panic("close of nil channel") 301 } 302 303 lock(&c.lock) 304 if c.closed != 0 { 305 unlock(&c.lock) 306 panic("close of closed channel") 307 } 308 309 if raceenabled { 310 callerpc := getcallerpc(unsafe.Pointer(&c)) 311 racewritepc(unsafe.Pointer(c), callerpc, funcPC(closechan)) 312 racerelease(unsafe.Pointer(c)) 313 } 314 315 c.closed = 1 316 317 // release all readers 318 for { 319 sg := c.recvq.dequeue() 320 if sg == nil { 321 break 322 } 323 gp := sg.g 324 sg.elem = nil 325 gp.param = nil 326 if sg.releasetime != 0 { 327 sg.releasetime = cputicks() 328 } 329 goready(gp, 3) 330 } 331 332 // release all writers 333 for { 334 sg := c.sendq.dequeue() 335 if sg == nil { 336 break 337 } 338 gp := sg.g 339 sg.elem = nil 340 gp.param = nil 341 if sg.releasetime != 0 { 342 sg.releasetime = cputicks() 343 } 344 goready(gp, 3) 345 } 346 unlock(&c.lock) 347 } 348 349 // entry points for <- c from compiled code 350 //go:nosplit 351 func chanrecv1(t *chantype, c *hchan, elem unsafe.Pointer) { 352 chanrecv(t, c, elem, true) 353 } 354 355 //go:nosplit 356 func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) { 357 _, received = chanrecv(t, c, elem, true) 358 return 359 } 360 361 // chanrecv receives on channel c and writes the received data to ep. 362 // ep may be nil, in which case received data is ignored. 363 // If block == false and no elements are available, returns (false, false). 364 // Otherwise, if c is closed, zeros *ep and returns (true, false). 365 // Otherwise, fills in *ep with an element and returns (true, true). 366 func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { 367 // raceenabled: don't need to check ep, as it is always on the stack. 368 369 if debugChan { 370 print("chanrecv: chan=", c, "\n") 371 } 372 373 if c == nil { 374 if !block { 375 return 376 } 377 gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2) 378 throw("unreachable") 379 } 380 381 // Fast path: check for failed non-blocking operation without acquiring the lock. 382 // 383 // After observing that the channel is not ready for receiving, we observe that the 384 // channel is not closed. Each of these observations is a single word-sized read 385 // (first c.sendq.first or c.qcount, and second c.closed). 386 // Because a channel cannot be reopened, the later observation of the channel 387 // being not closed implies that it was also not closed at the moment of the 388 // first observation. We behave as if we observed the channel at that moment 389 // and report that the receive cannot proceed. 390 // 391 // The order of operations is important here: reversing the operations can lead to 392 // incorrect behavior when racing with a close. 393 if !block && (c.dataqsiz == 0 && c.sendq.first == nil || 394 c.dataqsiz > 0 && atomicloaduint(&c.qcount) == 0) && 395 atomicload(&c.closed) == 0 { 396 return 397 } 398 399 var t0 int64 400 if blockprofilerate > 0 { 401 t0 = cputicks() 402 } 403 404 lock(&c.lock) 405 if c.dataqsiz == 0 { // synchronous channel 406 if c.closed != 0 { 407 return recvclosed(c, ep) 408 } 409 410 sg := c.sendq.dequeue() 411 if sg != nil { 412 if raceenabled { 413 racesync(c, sg) 414 } 415 unlock(&c.lock) 416 417 if ep != nil { 418 typedmemmove(c.elemtype, ep, sg.elem) 419 } 420 sg.elem = nil 421 gp := sg.g 422 gp.param = unsafe.Pointer(sg) 423 if sg.releasetime != 0 { 424 sg.releasetime = cputicks() 425 } 426 goready(gp, 3) 427 selected = true 428 received = true 429 return 430 } 431 432 if !block { 433 unlock(&c.lock) 434 return 435 } 436 437 // no sender available: block on this channel. 438 gp := getg() 439 mysg := acquireSudog() 440 mysg.releasetime = 0 441 if t0 != 0 { 442 mysg.releasetime = -1 443 } 444 mysg.elem = ep 445 mysg.waitlink = nil 446 gp.waiting = mysg 447 mysg.g = gp 448 mysg.selectdone = nil 449 gp.param = nil 450 c.recvq.enqueue(mysg) 451 goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3) 452 453 // someone woke us up 454 if mysg != gp.waiting { 455 throw("G waiting list is corrupted!") 456 } 457 gp.waiting = nil 458 if mysg.releasetime > 0 { 459 blockevent(mysg.releasetime-t0, 2) 460 } 461 haveData := gp.param != nil 462 gp.param = nil 463 releaseSudog(mysg) 464 465 if haveData { 466 // a sender sent us some data. It already wrote to ep. 467 selected = true 468 received = true 469 return 470 } 471 472 lock(&c.lock) 473 if c.closed == 0 { 474 throw("chanrecv: spurious wakeup") 475 } 476 return recvclosed(c, ep) 477 } 478 479 // asynchronous channel 480 // wait for some data to appear 481 var t1 int64 482 for futile := byte(0); c.qcount <= 0; futile = traceFutileWakeup { 483 if c.closed != 0 { 484 selected, received = recvclosed(c, ep) 485 if t1 > 0 { 486 blockevent(t1-t0, 2) 487 } 488 return 489 } 490 491 if !block { 492 unlock(&c.lock) 493 return 494 } 495 496 // wait for someone to send an element 497 gp := getg() 498 mysg := acquireSudog() 499 mysg.releasetime = 0 500 if t0 != 0 { 501 mysg.releasetime = -1 502 } 503 mysg.elem = nil 504 mysg.g = gp 505 mysg.selectdone = nil 506 507 c.recvq.enqueue(mysg) 508 goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv|futile, 3) 509 510 // someone woke us up - try again 511 if mysg.releasetime > 0 { 512 t1 = mysg.releasetime 513 } 514 releaseSudog(mysg) 515 lock(&c.lock) 516 } 517 518 if raceenabled { 519 raceacquire(chanbuf(c, c.recvx)) 520 racerelease(chanbuf(c, c.recvx)) 521 } 522 if ep != nil { 523 typedmemmove(c.elemtype, ep, chanbuf(c, c.recvx)) 524 } 525 memclr(chanbuf(c, c.recvx), uintptr(c.elemsize)) 526 527 c.recvx++ 528 if c.recvx == c.dataqsiz { 529 c.recvx = 0 530 } 531 c.qcount-- 532 533 // ping a sender now that there is space 534 sg := c.sendq.dequeue() 535 if sg != nil { 536 gp := sg.g 537 unlock(&c.lock) 538 if sg.releasetime != 0 { 539 sg.releasetime = cputicks() 540 } 541 goready(gp, 3) 542 } else { 543 unlock(&c.lock) 544 } 545 546 if t1 > 0 { 547 blockevent(t1-t0, 2) 548 } 549 selected = true 550 received = true 551 return 552 } 553 554 // recvclosed is a helper function for chanrecv. Handles cleanup 555 // when the receiver encounters a closed channel. 556 // Caller must hold c.lock, recvclosed will release the lock. 557 func recvclosed(c *hchan, ep unsafe.Pointer) (selected, recevied bool) { 558 if raceenabled { 559 raceacquire(unsafe.Pointer(c)) 560 } 561 unlock(&c.lock) 562 if ep != nil { 563 memclr(ep, uintptr(c.elemsize)) 564 } 565 return true, false 566 } 567 568 // compiler implements 569 // 570 // select { 571 // case c <- v: 572 // ... foo 573 // default: 574 // ... bar 575 // } 576 // 577 // as 578 // 579 // if selectnbsend(c, v) { 580 // ... foo 581 // } else { 582 // ... bar 583 // } 584 // 585 func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) { 586 return chansend(t, c, elem, false, getcallerpc(unsafe.Pointer(&t))) 587 } 588 589 // compiler implements 590 // 591 // select { 592 // case v = <-c: 593 // ... foo 594 // default: 595 // ... bar 596 // } 597 // 598 // as 599 // 600 // if selectnbrecv(&v, c) { 601 // ... foo 602 // } else { 603 // ... bar 604 // } 605 // 606 func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) { 607 selected, _ = chanrecv(t, c, elem, false) 608 return 609 } 610 611 // compiler implements 612 // 613 // select { 614 // case v, ok = <-c: 615 // ... foo 616 // default: 617 // ... bar 618 // } 619 // 620 // as 621 // 622 // if c != nil && selectnbrecv2(&v, &ok, c) { 623 // ... foo 624 // } else { 625 // ... bar 626 // } 627 // 628 func selectnbrecv2(t *chantype, elem unsafe.Pointer, received *bool, c *hchan) (selected bool) { 629 // TODO(khr): just return 2 values from this function, now that it is in Go. 630 selected, *received = chanrecv(t, c, elem, false) 631 return 632 } 633 634 //go:linkname reflect_chansend reflect.chansend 635 func reflect_chansend(t *chantype, c *hchan, elem unsafe.Pointer, nb bool) (selected bool) { 636 return chansend(t, c, elem, !nb, getcallerpc(unsafe.Pointer(&t))) 637 } 638 639 //go:linkname reflect_chanrecv reflect.chanrecv 640 func reflect_chanrecv(t *chantype, c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) { 641 return chanrecv(t, c, elem, !nb) 642 } 643 644 //go:linkname reflect_chanlen reflect.chanlen 645 func reflect_chanlen(c *hchan) int { 646 if c == nil { 647 return 0 648 } 649 return int(c.qcount) 650 } 651 652 //go:linkname reflect_chancap reflect.chancap 653 func reflect_chancap(c *hchan) int { 654 if c == nil { 655 return 0 656 } 657 return int(c.dataqsiz) 658 } 659 660 //go:linkname reflect_chanclose reflect.chanclose 661 func reflect_chanclose(c *hchan) { 662 closechan(c) 663 } 664 665 func (q *waitq) enqueue(sgp *sudog) { 666 sgp.next = nil 667 x := q.last 668 if x == nil { 669 sgp.prev = nil 670 q.first = sgp 671 q.last = sgp 672 return 673 } 674 sgp.prev = x 675 x.next = sgp 676 q.last = sgp 677 } 678 679 func (q *waitq) dequeue() *sudog { 680 for { 681 sgp := q.first 682 if sgp == nil { 683 return nil 684 } 685 y := sgp.next 686 if y == nil { 687 q.first = nil 688 q.last = nil 689 } else { 690 y.prev = nil 691 q.first = y 692 sgp.next = nil // mark as removed (see dequeueSudog) 693 } 694 695 // if sgp participates in a select and is already signaled, ignore it 696 if sgp.selectdone != nil { 697 // claim the right to signal 698 if *sgp.selectdone != 0 || !cas(sgp.selectdone, 0, 1) { 699 continue 700 } 701 } 702 703 return sgp 704 } 705 } 706 707 func racesync(c *hchan, sg *sudog) { 708 racerelease(chanbuf(c, 0)) 709 raceacquireg(sg.g, chanbuf(c, 0)) 710 racereleaseg(sg.g, chanbuf(c, 0)) 711 raceacquire(chanbuf(c, 0)) 712 }