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