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