mirror of
https://github.com/golang/go
synced 2024-11-21 18:54:43 -07:00
replace non-blocking send, receive syntax with select
R=golang-dev, nigeltao, niemeyer, r CC=golang-dev https://golang.org/cl/4079053
This commit is contained in:
parent
fc52d7029f
commit
f4e76d8309
@ -116,9 +116,16 @@ func (b *syncBuffer) Read(p []byte) (n int, err os.Error) {
|
|||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *syncBuffer) signal() {
|
||||||
|
select {
|
||||||
|
case b.ready <- true:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *syncBuffer) Write(p []byte) (n int, err os.Error) {
|
func (b *syncBuffer) Write(p []byte) (n int, err os.Error) {
|
||||||
n, err = b.buf.Write(p)
|
n, err = b.buf.Write(p)
|
||||||
_ = b.ready <- true
|
b.signal()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,12 +135,12 @@ func (b *syncBuffer) WriteMode() {
|
|||||||
|
|
||||||
func (b *syncBuffer) ReadMode() {
|
func (b *syncBuffer) ReadMode() {
|
||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
_ = b.ready <- true
|
b.signal()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *syncBuffer) Close() os.Error {
|
func (b *syncBuffer) Close() os.Error {
|
||||||
b.closed = true
|
b.closed = true
|
||||||
_ = b.ready <- true
|
b.signal()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,10 +122,13 @@ func (c *conn) writeSocket() {
|
|||||||
func (c *conn) Screen() draw.Image { return c.img }
|
func (c *conn) Screen() draw.Image { return c.img }
|
||||||
|
|
||||||
func (c *conn) FlushImage() {
|
func (c *conn) FlushImage() {
|
||||||
// We do the send (the <- operator) in an expression context, rather than in
|
select {
|
||||||
// a statement context, so that it does not block, and fails if the buffered
|
case c.flush <- false:
|
||||||
// channel is full (in which case there already is a flush request pending).
|
// Flush notification sent.
|
||||||
_ = c.flush <- false
|
default:
|
||||||
|
// Could not send.
|
||||||
|
// Flush notification must be pending already.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Close() os.Error {
|
func (c *conn) Close() os.Error {
|
||||||
|
@ -74,15 +74,42 @@ type pp struct {
|
|||||||
fmt fmt
|
fmt fmt
|
||||||
}
|
}
|
||||||
|
|
||||||
// A leaky bucket of reusable pp structures.
|
// A cache holds a set of reusable objects.
|
||||||
var ppFree = make(chan *pp, 100)
|
// The buffered channel holds the currently available objects.
|
||||||
|
// If more are needed, the cache creates them by calling new.
|
||||||
|
type cache struct {
|
||||||
|
saved chan interface{}
|
||||||
|
new func() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate a new pp struct. Probably can grab the previous one from ppFree.
|
func (c *cache) put(x interface{}) {
|
||||||
func newPrinter() *pp {
|
select {
|
||||||
p, ok := <-ppFree
|
case c.saved <- x:
|
||||||
if !ok {
|
// saved in cache
|
||||||
p = new(pp)
|
default:
|
||||||
|
// discard
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cache) get() interface{} {
|
||||||
|
select {
|
||||||
|
case x := <-c.saved:
|
||||||
|
return x // reused from cache
|
||||||
|
default:
|
||||||
|
return c.new()
|
||||||
|
}
|
||||||
|
panic("not reached")
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCache(f func() interface{}) *cache {
|
||||||
|
return &cache{make(chan interface{}, 100), f}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ppFree = newCache(func() interface{} { return new(pp) })
|
||||||
|
|
||||||
|
// Allocate a new pp struct or grab a cached one.
|
||||||
|
func newPrinter() *pp {
|
||||||
|
p := ppFree.get().(*pp)
|
||||||
p.fmt.init(&p.buf)
|
p.fmt.init(&p.buf)
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
@ -94,7 +121,7 @@ func (p *pp) free() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.buf.Reset()
|
p.buf.Reset()
|
||||||
_ = ppFree <- p
|
ppFree.put(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
|
func (p *pp) Width() (wid int, ok bool) { return p.fmt.wid, p.fmt.widPresent }
|
||||||
|
@ -303,15 +303,11 @@ func (r *readRune) ReadRune() (rune int, size int, err os.Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// A leaky bucket of reusable ss structures.
|
var ssFree = newCache(func() interface{} { return new(ss) })
|
||||||
var ssFree = make(chan *ss, 100)
|
|
||||||
|
|
||||||
// Allocate a new ss struct. Probably can grab the previous one from ssFree.
|
// Allocate a new ss struct or grab a cached one.
|
||||||
func newScanState(r io.Reader, nlIsSpace bool) *ss {
|
func newScanState(r io.Reader, nlIsSpace bool) *ss {
|
||||||
s, ok := <-ssFree
|
s := ssFree.get().(*ss)
|
||||||
if !ok {
|
|
||||||
s = new(ss)
|
|
||||||
}
|
|
||||||
if rr, ok := r.(readRuner); ok {
|
if rr, ok := r.(readRuner); ok {
|
||||||
s.rr = rr
|
s.rr = rr
|
||||||
} else {
|
} else {
|
||||||
@ -333,7 +329,7 @@ func (s *ss) free() {
|
|||||||
}
|
}
|
||||||
s.buf.Reset()
|
s.buf.Reset()
|
||||||
s.rr = nil
|
s.rr = nil
|
||||||
_ = ssFree <- s
|
ssFree.put(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// skipSpace skips spaces and maybe newlines.
|
// skipSpace skips spaces and maybe newlines.
|
||||||
|
@ -220,11 +220,16 @@ func (s *pollServer) Run() {
|
|||||||
nn, _ = s.pr.Read(scratch[0:])
|
nn, _ = s.pr.Read(scratch[0:])
|
||||||
}
|
}
|
||||||
// Read from channels
|
// Read from channels
|
||||||
for fd, ok := <-s.cr; ok; fd, ok = <-s.cr {
|
Update:
|
||||||
s.AddFD(fd, 'r')
|
for {
|
||||||
}
|
select {
|
||||||
for fd, ok := <-s.cw; ok; fd, ok = <-s.cw {
|
case fd := <-s.cr:
|
||||||
s.AddFD(fd, 'w')
|
s.AddFD(fd, 'r')
|
||||||
|
case fd := <-s.cw:
|
||||||
|
s.AddFD(fd, 'w')
|
||||||
|
default:
|
||||||
|
break Update
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
netfd := s.LookupFD(fd, mode)
|
netfd := s.LookupFD(fd, mode)
|
||||||
|
@ -140,13 +140,16 @@ func runPacket(t *testing.T, network, addr string, listening chan<- string, done
|
|||||||
listening <- c.LocalAddr().String()
|
listening <- c.LocalAddr().String()
|
||||||
c.SetReadTimeout(10e6) // 10ms
|
c.SetReadTimeout(10e6) // 10ms
|
||||||
var buf [1000]byte
|
var buf [1000]byte
|
||||||
|
Run:
|
||||||
for {
|
for {
|
||||||
n, addr, err := c.ReadFrom(buf[0:])
|
n, addr, err := c.ReadFrom(buf[0:])
|
||||||
if e, ok := err.(Error); ok && e.Timeout() {
|
if e, ok := err.(Error); ok && e.Timeout() {
|
||||||
if done <- 1 {
|
select {
|
||||||
break
|
case done <- 1:
|
||||||
|
break Run
|
||||||
|
default:
|
||||||
|
continue Run
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
|
@ -256,7 +256,10 @@ func (nch *netChan) send(val reflect.Value) {
|
|||||||
nch.sendCh = make(chan reflect.Value, nch.size)
|
nch.sendCh = make(chan reflect.Value, nch.size)
|
||||||
go nch.sender()
|
go nch.sender()
|
||||||
}
|
}
|
||||||
if ok := nch.sendCh <- val; !ok {
|
select {
|
||||||
|
case nch.sendCh <- val:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
// TODO: should this be more resilient?
|
// TODO: should this be more resilient?
|
||||||
panic("netchan: remote sender sent more values than allowed")
|
panic("netchan: remote sender sent more values than allowed")
|
||||||
}
|
}
|
||||||
@ -318,8 +321,11 @@ func (nch *netChan) acked() {
|
|||||||
if nch.dir != Send {
|
if nch.dir != Send {
|
||||||
panic("recv on wrong direction of channel")
|
panic("recv on wrong direction of channel")
|
||||||
}
|
}
|
||||||
if ok := nch.ackCh <- true; !ok {
|
select {
|
||||||
panic("netchan: remote receiver sent too many acks")
|
case nch.ackCh <- true:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
// TODO: should this be more resilient?
|
// TODO: should this be more resilient?
|
||||||
|
panic("netchan: remote receiver sent too many acks")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,13 @@ func (imp *Importer) run() {
|
|||||||
}
|
}
|
||||||
if err.Error != "" {
|
if err.Error != "" {
|
||||||
impLog("response error:", err.Error)
|
impLog("response error:", err.Error)
|
||||||
if sent := imp.errors <- os.ErrorString(err.Error); !sent {
|
select {
|
||||||
|
case imp.errors <- os.ErrorString(err.Error):
|
||||||
|
continue // errors are not acknowledged
|
||||||
|
default:
|
||||||
imp.shutdown()
|
imp.shutdown()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
continue // errors are not acknowledged.
|
|
||||||
}
|
}
|
||||||
case payClosed:
|
case payClosed:
|
||||||
nch := imp.getChan(hdr.Id, false)
|
nch := imp.getChan(hdr.Id, false)
|
||||||
|
@ -153,7 +153,11 @@ func (w *Watcher) readEvents() {
|
|||||||
for {
|
for {
|
||||||
n, errno = syscall.Read(w.fd, buf[0:])
|
n, errno = syscall.Read(w.fd, buf[0:])
|
||||||
// See if there is a message on the "done" channel
|
// See if there is a message on the "done" channel
|
||||||
_, done := <-w.done
|
var done bool
|
||||||
|
select {
|
||||||
|
case done = <-w.done:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
// If EOF or a "done" message is received
|
// If EOF or a "done" message is received
|
||||||
if n == 0 || done {
|
if n == 0 || done {
|
||||||
|
@ -256,8 +256,11 @@ func TestWalk(t *testing.T) {
|
|||||||
// 2) handle errors, expect none
|
// 2) handle errors, expect none
|
||||||
errors := make(chan os.Error, 64)
|
errors := make(chan os.Error, 64)
|
||||||
Walk(tree.name, v, errors)
|
Walk(tree.name, v, errors)
|
||||||
if err, ok := <-errors; ok {
|
select {
|
||||||
|
case err := <-errors:
|
||||||
t.Errorf("no error expected, found: %s", err)
|
t.Errorf("no error expected, found: %s", err)
|
||||||
|
default:
|
||||||
|
// ok
|
||||||
}
|
}
|
||||||
checkMarks(t)
|
checkMarks(t)
|
||||||
|
|
||||||
@ -276,14 +279,21 @@ func TestWalk(t *testing.T) {
|
|||||||
errors = make(chan os.Error, 64)
|
errors = make(chan os.Error, 64)
|
||||||
os.Chmod(Join(tree.name, tree.entries[1].name), 0)
|
os.Chmod(Join(tree.name, tree.entries[1].name), 0)
|
||||||
Walk(tree.name, v, errors)
|
Walk(tree.name, v, errors)
|
||||||
|
Loop:
|
||||||
for i := 1; i <= 2; i++ {
|
for i := 1; i <= 2; i++ {
|
||||||
if _, ok := <-errors; !ok {
|
select {
|
||||||
|
case <-errors:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
t.Errorf("%d. error expected, none found", i)
|
t.Errorf("%d. error expected, none found", i)
|
||||||
break
|
break Loop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err, ok := <-errors; ok {
|
select {
|
||||||
|
case err := <-errors:
|
||||||
t.Errorf("only two errors expected, found 3rd: %v", err)
|
t.Errorf("only two errors expected, found 3rd: %v", err)
|
||||||
|
default:
|
||||||
|
// ok
|
||||||
}
|
}
|
||||||
// the inaccessible subtrees were marked manually
|
// the inaccessible subtrees were marked manually
|
||||||
checkMarks(t)
|
checkMarks(t)
|
||||||
|
@ -58,7 +58,7 @@ func (client *Client) send(c *Call) {
|
|||||||
if client.shutdown != nil {
|
if client.shutdown != nil {
|
||||||
c.Error = client.shutdown
|
c.Error = client.shutdown
|
||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
_ = c.Done <- c // do not block
|
c.done()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.seq = client.seq
|
c.seq = client.seq
|
||||||
@ -102,16 +102,14 @@ func (client *Client) input() {
|
|||||||
// Empty strings should turn into nil os.Errors
|
// Empty strings should turn into nil os.Errors
|
||||||
c.Error = nil
|
c.Error = nil
|
||||||
}
|
}
|
||||||
// We don't want to block here. It is the caller's responsibility to make
|
c.done()
|
||||||
// sure the channel has enough buffer space. See comment in Go().
|
|
||||||
_ = c.Done <- c // do not block
|
|
||||||
}
|
}
|
||||||
// Terminate pending calls.
|
// Terminate pending calls.
|
||||||
client.mutex.Lock()
|
client.mutex.Lock()
|
||||||
client.shutdown = err
|
client.shutdown = err
|
||||||
for _, call := range client.pending {
|
for _, call := range client.pending {
|
||||||
call.Error = err
|
call.Error = err
|
||||||
_ = call.Done <- call // do not block
|
call.done()
|
||||||
}
|
}
|
||||||
client.mutex.Unlock()
|
client.mutex.Unlock()
|
||||||
if err != os.EOF || !client.closing {
|
if err != os.EOF || !client.closing {
|
||||||
@ -119,6 +117,16 @@ func (client *Client) input() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (call *Call) done() {
|
||||||
|
select {
|
||||||
|
case call.Done <- call:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
// We don't want to block here. It is the caller's responsibility to make
|
||||||
|
// sure the channel has enough buffer space. See comment in Go().
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewClient returns a new Client to handle requests to the
|
// NewClient returns a new Client to handle requests to the
|
||||||
// set of services at the other end of the connection.
|
// set of services at the other end of the connection.
|
||||||
func NewClient(conn io.ReadWriteCloser) *Client {
|
func NewClient(conn io.ReadWriteCloser) *Client {
|
||||||
@ -233,7 +241,7 @@ func (client *Client) Go(serviceMethod string, args interface{}, reply interface
|
|||||||
c.Done = done
|
c.Done = done
|
||||||
if client.shutdown != nil {
|
if client.shutdown != nil {
|
||||||
c.Error = client.shutdown
|
c.Error = client.shutdown
|
||||||
_ = c.Done <- c // do not block
|
c.done()
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
client.send(c)
|
client.send(c)
|
||||||
|
@ -364,14 +364,12 @@ func TestSendDeadlock(t *testing.T) {
|
|||||||
testSendDeadlock(client)
|
testSendDeadlock(client)
|
||||||
done <- true
|
done <- true
|
||||||
}()
|
}()
|
||||||
for i := 0; i < 50; i++ {
|
select {
|
||||||
time.Sleep(100 * 1e6)
|
case <-done:
|
||||||
_, ok := <-done
|
return
|
||||||
if ok {
|
case <-time.After(5e9):
|
||||||
return
|
t.Fatal("deadlock")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.Fatal("deadlock")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSendDeadlock(client *Client) {
|
func testSendDeadlock(client *Client) {
|
||||||
|
@ -120,10 +120,12 @@ func TestAfterStop(t *testing.T) {
|
|||||||
t.Fatalf("failed to stop event 1")
|
t.Fatalf("failed to stop event 1")
|
||||||
}
|
}
|
||||||
<-c2
|
<-c2
|
||||||
_, ok0 := <-t0.C
|
select {
|
||||||
_, ok1 := <-c1
|
case <-t0.C:
|
||||||
if ok0 || ok1 {
|
t.Fatalf("event 0 was not stopped")
|
||||||
t.Fatalf("events were not stopped")
|
case <-c1:
|
||||||
|
t.Fatalf("event 1 was not stopped")
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
if t1.Stop() {
|
if t1.Stop() {
|
||||||
t.Fatalf("Stop returned true twice")
|
t.Fatalf("Stop returned true twice")
|
||||||
|
@ -22,8 +22,12 @@ type Ticker struct {
|
|||||||
|
|
||||||
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
// Stop turns off a ticker. After Stop, no more ticks will be sent.
|
||||||
func (t *Ticker) Stop() {
|
func (t *Ticker) Stop() {
|
||||||
// Make it non-blocking so multiple Stops don't block.
|
select {
|
||||||
_ = t.shutdown <- true
|
case t.shutdown <- true:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
// Stop in progress already
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tick is a convenience wrapper for NewTicker providing access to the ticking
|
// Tick is a convenience wrapper for NewTicker providing access to the ticking
|
||||||
@ -106,7 +110,8 @@ func tickerLoop() {
|
|||||||
// that need it and determining the next wake time.
|
// that need it and determining the next wake time.
|
||||||
// TODO(r): list should be sorted in time order.
|
// TODO(r): list should be sorted in time order.
|
||||||
for t := tickers; t != nil; t = t.next {
|
for t := tickers; t != nil; t = t.next {
|
||||||
if _, ok := <-t.shutdown; ok {
|
select {
|
||||||
|
case <-t.shutdown:
|
||||||
// Ticker is done; remove it from list.
|
// Ticker is done; remove it from list.
|
||||||
if prev == nil {
|
if prev == nil {
|
||||||
tickers = t.next
|
tickers = t.next
|
||||||
@ -114,6 +119,7 @@ func tickerLoop() {
|
|||||||
prev.next = t.next
|
prev.next = t.next
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
if t.nextTick <= now {
|
if t.nextTick <= now {
|
||||||
if len(t.c) == 0 {
|
if len(t.c) == 0 {
|
||||||
|
@ -29,9 +29,11 @@ func TestTicker(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// Now test that the ticker stopped
|
// Now test that the ticker stopped
|
||||||
Sleep(2 * Delta)
|
Sleep(2 * Delta)
|
||||||
_, received := <-ticker.C
|
select {
|
||||||
if received {
|
case <-ticker.C:
|
||||||
t.Fatal("Ticker did not shut down")
|
t.Fatal("Ticker did not shut down")
|
||||||
|
default:
|
||||||
|
// ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,6 @@ func main() {
|
|||||||
var i64 int64
|
var i64 int64
|
||||||
var b bool
|
var b bool
|
||||||
var s string
|
var s string
|
||||||
var ok bool
|
|
||||||
|
|
||||||
var sync = make(chan bool)
|
var sync = make(chan bool)
|
||||||
|
|
||||||
@ -86,35 +85,45 @@ func main() {
|
|||||||
cb := make(chan bool, buffer)
|
cb := make(chan bool, buffer)
|
||||||
cs := make(chan string, buffer)
|
cs := make(chan string, buffer)
|
||||||
|
|
||||||
i32, ok = <-c32
|
select {
|
||||||
if ok {
|
case i32 = <-c32:
|
||||||
panic("blocked i32sender")
|
panic("blocked i32sender")
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
i64, ok = <-c64
|
select {
|
||||||
if ok {
|
case i64 = <-c64:
|
||||||
panic("blocked i64sender")
|
panic("blocked i64sender")
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
b, ok = <-cb
|
select {
|
||||||
if ok {
|
case b = <-cb:
|
||||||
panic("blocked bsender")
|
panic("blocked bsender")
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
s, ok = <-cs
|
select {
|
||||||
if ok {
|
case s = <-cs:
|
||||||
panic("blocked ssender")
|
panic("blocked ssender")
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
go i32receiver(c32, sync)
|
go i32receiver(c32, sync)
|
||||||
try := 0
|
try := 0
|
||||||
for !(c32 <- 123) {
|
Send32:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
println("i32receiver buffer=", buffer)
|
case c32 <- 123:
|
||||||
panic("fail")
|
break Send32
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
println("i32receiver buffer=", buffer)
|
||||||
|
panic("fail")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
<-sync
|
<-sync
|
||||||
|
|
||||||
@ -123,13 +132,19 @@ func main() {
|
|||||||
<-sync
|
<-sync
|
||||||
}
|
}
|
||||||
try = 0
|
try = 0
|
||||||
for i32, ok = <-c32; !ok; i32, ok = <-c32 {
|
Recv32:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
println("i32sender buffer=", buffer)
|
case i32 = <-c32:
|
||||||
panic("fail")
|
break Recv32
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
println("i32sender buffer=", buffer)
|
||||||
|
panic("fail")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
if i32 != 234 {
|
if i32 != 234 {
|
||||||
panic("i32sender value")
|
panic("i32sender value")
|
||||||
@ -140,12 +155,18 @@ func main() {
|
|||||||
|
|
||||||
go i64receiver(c64, sync)
|
go i64receiver(c64, sync)
|
||||||
try = 0
|
try = 0
|
||||||
for !(c64 <- 123456) {
|
Send64:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("i64receiver")
|
case c64 <- 123456:
|
||||||
|
break Send64
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("i64receiver")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
<-sync
|
<-sync
|
||||||
|
|
||||||
@ -154,12 +175,18 @@ func main() {
|
|||||||
<-sync
|
<-sync
|
||||||
}
|
}
|
||||||
try = 0
|
try = 0
|
||||||
for i64, ok = <-c64; !ok; i64, ok = <-c64 {
|
Recv64:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("i64sender")
|
case i64 = <-c64:
|
||||||
|
break Recv64
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("i64sender")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
if i64 != 234567 {
|
if i64 != 234567 {
|
||||||
panic("i64sender value")
|
panic("i64sender value")
|
||||||
@ -170,12 +197,18 @@ func main() {
|
|||||||
|
|
||||||
go breceiver(cb, sync)
|
go breceiver(cb, sync)
|
||||||
try = 0
|
try = 0
|
||||||
for !(cb <- true) {
|
SendBool:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("breceiver")
|
case cb <- true:
|
||||||
|
break SendBool
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("breceiver")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
<-sync
|
<-sync
|
||||||
|
|
||||||
@ -184,12 +217,18 @@ func main() {
|
|||||||
<-sync
|
<-sync
|
||||||
}
|
}
|
||||||
try = 0
|
try = 0
|
||||||
for b, ok = <-cb; !ok; b, ok = <-cb {
|
RecvBool:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("bsender")
|
case b = <-cb:
|
||||||
|
break RecvBool
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("bsender")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
if !b {
|
if !b {
|
||||||
panic("bsender value")
|
panic("bsender value")
|
||||||
@ -200,12 +239,18 @@ func main() {
|
|||||||
|
|
||||||
go sreceiver(cs, sync)
|
go sreceiver(cs, sync)
|
||||||
try = 0
|
try = 0
|
||||||
for !(cs <- "hello") {
|
SendString:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("sreceiver")
|
case cs <- "hello":
|
||||||
|
break SendString
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("sreceiver")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
<-sync
|
<-sync
|
||||||
|
|
||||||
@ -214,12 +259,18 @@ func main() {
|
|||||||
<-sync
|
<-sync
|
||||||
}
|
}
|
||||||
try = 0
|
try = 0
|
||||||
for s, ok = <-cs; !ok; s, ok = <-cs {
|
RecvString:
|
||||||
try++
|
for {
|
||||||
if try > maxTries {
|
select {
|
||||||
panic("ssender")
|
case s = <-cs:
|
||||||
|
break RecvString
|
||||||
|
default:
|
||||||
|
try++
|
||||||
|
if try > maxTries {
|
||||||
|
panic("ssender")
|
||||||
|
}
|
||||||
|
sleep()
|
||||||
}
|
}
|
||||||
sleep()
|
|
||||||
}
|
}
|
||||||
if s != "hello again" {
|
if s != "hello again" {
|
||||||
panic("ssender value")
|
panic("ssender value")
|
||||||
|
@ -9,49 +9,46 @@ package main
|
|||||||
var (
|
var (
|
||||||
cr <-chan int
|
cr <-chan int
|
||||||
cs chan<- int
|
cs chan<- int
|
||||||
c chan int
|
c chan int
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cr = c // ok
|
cr = c // ok
|
||||||
cs = c // ok
|
cs = c // ok
|
||||||
c = cr // ERROR "illegal types|incompatible|cannot"
|
c = cr // ERROR "illegal types|incompatible|cannot"
|
||||||
c = cs // ERROR "illegal types|incompatible|cannot"
|
c = cs // ERROR "illegal types|incompatible|cannot"
|
||||||
cr = cs // ERROR "illegal types|incompatible|cannot"
|
cr = cs // ERROR "illegal types|incompatible|cannot"
|
||||||
cs = cr // ERROR "illegal types|incompatible|cannot"
|
cs = cr // ERROR "illegal types|incompatible|cannot"
|
||||||
|
|
||||||
c <- 0 // ok
|
c <- 0 // ok
|
||||||
ok := c <- 0 // ok
|
<-c // ok
|
||||||
_ = ok
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
<-c // ok
|
// x, ok := <-c // ok
|
||||||
x, ok := <-c // ok
|
// _, _ = x, ok
|
||||||
_, _ = x, ok
|
|
||||||
|
|
||||||
cr <- 0 // ERROR "send"
|
cr <- 0 // ERROR "send"
|
||||||
ok = cr <- 0 // ERROR "send"
|
<-cr // ok
|
||||||
_ = ok
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
<-cr // ok
|
// x, ok = <-cr // ok
|
||||||
x, ok = <-cr // ok
|
// _, _ = x, ok
|
||||||
_, _ = x, ok
|
|
||||||
|
|
||||||
cs <- 0 // ok
|
cs <- 0 // ok
|
||||||
ok = cs <- 0 // ok
|
<-cs // ERROR "receive"
|
||||||
_ = ok
|
////TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
<-cs // ERROR "receive"
|
//// x, ok = <-cs // ERROR "receive"
|
||||||
x, ok = <-cs // ERROR "receive"
|
//// _, _ = x, ok
|
||||||
_, _ = x, ok
|
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case c <- 0: // ok
|
case c <- 0: // ok
|
||||||
case x := <-c: // ok
|
case x := <-c: // ok
|
||||||
_ = x
|
_ = x
|
||||||
|
|
||||||
case cr <- 0: // ERROR "send"
|
case cr <- 0: // ERROR "send"
|
||||||
case x := <-cr: // ok
|
case x := <-cr: // ok
|
||||||
_ = x
|
_ = x
|
||||||
|
|
||||||
case cs <- 0: // ok
|
case cs <- 0: // ok
|
||||||
case x := <-cs: // ERROR "receive"
|
case x := <-cs: // ERROR "receive"
|
||||||
_ = x
|
_ = x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,21 @@ type Chan interface {
|
|||||||
Impl() string
|
Impl() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// direct channel operations
|
// direct channel operations when possible
|
||||||
type XChan chan int
|
type XChan chan int
|
||||||
|
|
||||||
func (c XChan) Send(x int) {
|
func (c XChan) Send(x int) {
|
||||||
c <- x
|
c <- x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c XChan) Nbsend(x int) bool {
|
func (c XChan) Nbsend(x int) bool {
|
||||||
return c <- x
|
select {
|
||||||
|
case c <- x:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
panic("nbsend")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c XChan) Recv() int {
|
func (c XChan) Recv() int {
|
||||||
@ -36,8 +43,13 @@ func (c XChan) Recv() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c XChan) Nbrecv() (int, bool) {
|
func (c XChan) Nbrecv() (int, bool) {
|
||||||
x, ok := <-c
|
select {
|
||||||
return x, ok
|
case x := <-c:
|
||||||
|
return x, true
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
panic("nbrecv")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c XChan) Close() {
|
func (c XChan) Close() {
|
||||||
@ -54,6 +66,7 @@ func (c XChan) Impl() string {
|
|||||||
|
|
||||||
// indirect operations via select
|
// indirect operations via select
|
||||||
type SChan chan int
|
type SChan chan int
|
||||||
|
|
||||||
func (c SChan) Send(x int) {
|
func (c SChan) Send(x int) {
|
||||||
select {
|
select {
|
||||||
case c <- x:
|
case c <- x:
|
||||||
@ -62,10 +75,10 @@ func (c SChan) Send(x int) {
|
|||||||
|
|
||||||
func (c SChan) Nbsend(x int) bool {
|
func (c SChan) Nbsend(x int) bool {
|
||||||
select {
|
select {
|
||||||
case c <- x:
|
|
||||||
return true
|
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
case c <- x:
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
panic("nbsend")
|
panic("nbsend")
|
||||||
}
|
}
|
||||||
@ -80,10 +93,10 @@ func (c SChan) Recv() int {
|
|||||||
|
|
||||||
func (c SChan) Nbrecv() (int, bool) {
|
func (c SChan) Nbrecv() (int, bool) {
|
||||||
select {
|
select {
|
||||||
case x := <-c:
|
|
||||||
return x, true
|
|
||||||
default:
|
default:
|
||||||
return 0, false
|
return 0, false
|
||||||
|
case x := <-c:
|
||||||
|
return x, true
|
||||||
}
|
}
|
||||||
panic("nbrecv")
|
panic("nbrecv")
|
||||||
}
|
}
|
||||||
@ -100,6 +113,62 @@ func (c SChan) Impl() string {
|
|||||||
return "(select)"
|
return "(select)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// indirect operations via larger selects
|
||||||
|
var dummy = make(chan bool)
|
||||||
|
|
||||||
|
type SSChan chan int
|
||||||
|
|
||||||
|
func (c SSChan) Send(x int) {
|
||||||
|
select {
|
||||||
|
case c <- x:
|
||||||
|
case <-dummy:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Nbsend(x int) bool {
|
||||||
|
select {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case <-dummy:
|
||||||
|
case c <- x:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
panic("nbsend")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Recv() int {
|
||||||
|
select {
|
||||||
|
case <-dummy:
|
||||||
|
case x := <-c:
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
panic("recv")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Nbrecv() (int, bool) {
|
||||||
|
select {
|
||||||
|
case <-dummy:
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
case x := <-c:
|
||||||
|
return x, true
|
||||||
|
}
|
||||||
|
panic("nbrecv")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Close() {
|
||||||
|
close(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Closed() bool {
|
||||||
|
return closed(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c SSChan) Impl() string {
|
||||||
|
return "(select)"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
func shouldPanic(f func()) {
|
func shouldPanic(f func()) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if recover() == nil {
|
if recover() == nil {
|
||||||
@ -137,7 +206,7 @@ func test1(c Chan) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send should work with ,ok too: sent a value without blocking, so ok == true.
|
// send should work with ,ok too: sent a value without blocking, so ok == true.
|
||||||
shouldPanic(func(){c.Nbsend(1)})
|
shouldPanic(func() { c.Nbsend(1) })
|
||||||
|
|
||||||
// the value should have been discarded.
|
// the value should have been discarded.
|
||||||
if x := c.Recv(); x != 0 {
|
if x := c.Recv(); x != 0 {
|
||||||
@ -145,7 +214,7 @@ func test1(c Chan) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// similarly Send.
|
// similarly Send.
|
||||||
shouldPanic(func(){c.Send(2)})
|
shouldPanic(func() { c.Send(2) })
|
||||||
if x := c.Recv(); x != 0 {
|
if x := c.Recv(); x != 0 {
|
||||||
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
||||||
}
|
}
|
||||||
@ -195,9 +264,12 @@ func closedasync() chan int {
|
|||||||
func main() {
|
func main() {
|
||||||
test1(XChan(closedsync()))
|
test1(XChan(closedsync()))
|
||||||
test1(SChan(closedsync()))
|
test1(SChan(closedsync()))
|
||||||
|
test1(SSChan(closedsync()))
|
||||||
|
|
||||||
testasync1(XChan(closedasync()))
|
testasync1(XChan(closedasync()))
|
||||||
testasync1(SChan(closedasync()))
|
testasync1(SChan(closedasync()))
|
||||||
|
testasync1(SSChan(closedasync()))
|
||||||
testasync2(XChan(closedasync()))
|
testasync2(XChan(closedasync()))
|
||||||
testasync2(SChan(closedasync()))
|
testasync2(SChan(closedasync()))
|
||||||
|
testasync2(SSChan(closedasync()))
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ sub chk {
|
|||||||
my @match;
|
my @match;
|
||||||
foreach my $src (@{$src{$file}}) {
|
foreach my $src (@{$src{$file}}) {
|
||||||
$line++;
|
$line++;
|
||||||
|
next if $src =~ m|////|; # double comment disables ERROR
|
||||||
next unless $src =~ m|// (GC_)?ERROR (.*)|;
|
next unless $src =~ m|// (GC_)?ERROR (.*)|;
|
||||||
$regexp = $2;
|
$regexp = $2;
|
||||||
if($regexp !~ /^"([^"]*)"/) {
|
if($regexp !~ /^"([^"]*)"/) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// ! $G $D/$F.go
|
// errchk $G -e $D/$F.go
|
||||||
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
@ -7,8 +7,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var i int = 100;
|
var i int = 100
|
||||||
i = i << -3; // BUG: should not compile (negative shift)
|
i = i << -3 // ERROR "overflows"
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -6,15 +6,16 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
func main(){
|
func main() {
|
||||||
c := make(chan int);
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
ok := false;
|
// c := make(chan int);
|
||||||
var i int;
|
// ok := false;
|
||||||
|
// var i int;
|
||||||
i, ok = <-c; // works
|
//
|
||||||
_, _ = i, ok;
|
// i, ok = <-c; // works
|
||||||
|
// _, _ = i, ok;
|
||||||
ca := new([2]chan int);
|
//
|
||||||
i, ok = <-(ca[0]); // fails: c.go:11: bad shape across assignment - cr=1 cl=2
|
// ca := new([2]chan int);
|
||||||
_, _ = i, ok;
|
// i, ok = <-(ca[0]); // fails: c.go:11: bad shape across assignment - cr=1 cl=2
|
||||||
|
// _, _ = i, ok;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
// ! $G $D/$F.go
|
// errchk $G $D/$F.go
|
||||||
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved.
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
const x x = 2;
|
const x x = 2 // ERROR "loop"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bug081.go:3: first constant must evaluate an expression
|
bug081.go:3: first constant must evaluate an expression
|
||||||
|
@ -13,11 +13,12 @@ var i int
|
|||||||
func multi() (int, int) { return 1, 2 }
|
func multi() (int, int) { return 1, 2 }
|
||||||
|
|
||||||
func xxx() {
|
func xxx() {
|
||||||
var c chan int
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
x, ok := <-c
|
// var c chan int
|
||||||
|
// x, ok := <-c
|
||||||
|
|
||||||
var m map[int]int
|
var m map[int]int
|
||||||
x, ok = m[1]
|
x, ok := m[1]
|
||||||
|
|
||||||
var i interface{}
|
var i interface{}
|
||||||
var xx int
|
var xx int
|
||||||
|
@ -7,16 +7,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := make(chan int, 1)
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
c <- 100
|
// c := make(chan int, 1)
|
||||||
x, ok := <-c
|
// c <- 100
|
||||||
if x != 100 || !ok {
|
// x, ok := <-c
|
||||||
println("x=", x, " ok=", ok, " want 100, true")
|
// if x != 100 || !ok {
|
||||||
panic("fail")
|
// println("x=", x, " ok=", ok, " want 100, true")
|
||||||
}
|
// panic("fail")
|
||||||
x, ok = <-c
|
// }
|
||||||
if x != 0 || ok {
|
// x, ok = <-c
|
||||||
println("x=", x, " ok=", ok, " want 0, false")
|
// if x != 0 || ok {
|
||||||
panic("fail")
|
// println("x=", x, " ok=", ok, " want 0, false")
|
||||||
}
|
// panic("fail")
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,13 @@ func main() {
|
|||||||
|
|
||||||
c := make(chan byte, 1)
|
c := make(chan byte, 1)
|
||||||
c <- 'C'
|
c <- 'C'
|
||||||
|
//TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
// 15 16
|
// 15 16
|
||||||
*f(), p1 = <-e1(c, 16)
|
// *f(), p1 = <-e1(c, 16)
|
||||||
|
*f(), p1 = <-e1(c, 16), true // delete uncommenting above
|
||||||
// 17 18
|
// 17 18
|
||||||
*f(), p2 = <-e1(c, 18)
|
// *f(), p2 = <-e1(c, 18)
|
||||||
|
*f(), p2, _ = 0, false, e1(c, 18) // delete when uncommenting above
|
||||||
a[17] += '0'
|
a[17] += '0'
|
||||||
if !p1 || p2 {
|
if !p1 || p2 {
|
||||||
println("bad chan check", i, p1, p2)
|
println("bad chan check", i, p1, p2)
|
||||||
|
@ -123,9 +123,6 @@ panic: interface conversion: *main.S is not main.I2: missing method Name
|
|||||||
|
|
||||||
== fixedbugs/
|
== fixedbugs/
|
||||||
|
|
||||||
=========== fixedbugs/bug016.go
|
|
||||||
fixedbugs/bug016.go:11: constant -3 overflows uint
|
|
||||||
|
|
||||||
=========== fixedbugs/bug027.go
|
=========== fixedbugs/bug027.go
|
||||||
hi
|
hi
|
||||||
0 44444
|
0 44444
|
||||||
@ -148,9 +145,6 @@ inner loop top i 0
|
|||||||
do break
|
do break
|
||||||
broke
|
broke
|
||||||
|
|
||||||
=========== fixedbugs/bug081.go
|
|
||||||
fixedbugs/bug081.go:9: typechecking loop
|
|
||||||
|
|
||||||
=========== fixedbugs/bug093.go
|
=========== fixedbugs/bug093.go
|
||||||
M
|
M
|
||||||
|
|
||||||
|
@ -43,12 +43,9 @@ func main() {
|
|||||||
_, b = m[2] // ERROR "cannot .* bool.*type Bool"
|
_, b = m[2] // ERROR "cannot .* bool.*type Bool"
|
||||||
m[2] = 1, b // ERROR "cannot use.*type Bool.*as type bool"
|
m[2] = 1, b // ERROR "cannot use.*type Bool.*as type bool"
|
||||||
|
|
||||||
b = c <- 1 // ERROR "cannot use.*type bool.*type Bool"
|
////TODO(rsc): uncomment when this syntax is valid for receive+check closed
|
||||||
_ = b
|
//// _, b = <-c // ERROR "cannot .* bool.*type Bool"
|
||||||
asBool(c <- 1) // ERROR "cannot use.*type bool.*as type Bool"
|
//// _ = b
|
||||||
|
|
||||||
_, b = <-c // ERROR "cannot .* bool.*type Bool"
|
|
||||||
_ = b
|
|
||||||
|
|
||||||
var inter interface{}
|
var inter interface{}
|
||||||
_, b = inter.(Map) // ERROR "cannot .* bool.*type Bool"
|
_, b = inter.(Map) // ERROR "cannot .* bool.*type Bool"
|
||||||
|
Loading…
Reference in New Issue
Block a user