SnoopyGuo
SnoopyGuo

Reputation: 305

Monitor fullness of any channel in Go

There are several channels to monitor, their type are different and irrelevant(since we only care about len and cap), but golang compiler does not accept following code, whatever T is:

func monitorChan(ch chan T) {
    for {
        if len(ch) == cap(ch) {
            log.Warn("log")
        }
        time.Sleep(chanMonitorInterval)
    }
}

it shows error:

cannot use ch (type chan []byte) as type chan interface {} in argument to monitorChan.

How can this function be modified to write once monitor every channel?


Here is my code:

package main

import (
    "fmt"
    "time"
)

func monitorChan(ch chan interface{}) {
    for {
        if len(ch) == cap(ch) {
            fmt.Println("log")
        }
        time.Sleep(1 * time.Second)
    }
}

func main() {
    ch := make(chan []byte, 100)
    go monitorChan(ch)
    // actual things below ...
}

Playground: https://play.golang.org/p/t7T28IpLNAs

Upvotes: 1

Views: 568

Answers (3)

blackgreen
blackgreen

Reputation: 45120

Go 1.18

This is now trivial to accomplish, using type parameters:

func monitorChan[T any](ch chan T) {
    for {
        if len(ch) == cap(ch) {
            log.Warn("log")
        }
        time.Sleep(chanMonitorInterval)
    }
}

Type inference also allows to infer the type parameter T from the concrete type of the function argument, so the code in main doesn't even need to be rewritten:

func main() {
    ch := make(chan []byte, 100)
    go monitorChan(ch) // T instantiated as []byte
    // actual things below ...
}

Upvotes: 2

peterSO
peterSO

Reputation: 166835

Use reflection. For example,

package main

import (
    "log"
    "reflect"
    "time"
)

func monitorChan(ch interface{}, intvl time.Duration) {
    v := reflect.ValueOf(ch)
    if v.Kind() != reflect.Chan {
        return
    }

    c := v.Cap()
    if c == 0 {
        return
    }
    for {
        if l := v.Len(); l == c {
            log.Printf("log: len(%d) cap(%d)", l, c)
        }
        time.Sleep(intvl)
    }
}

func main() {
    log.Print("main")
    c := make(chan []byte, 10)
    var chanMonitorInterval = 1 * time.Second
    go monitorChan(c, chanMonitorInterval)
    log.Print("monitor")

    time.Sleep(5 * chanMonitorInterval)
    for len(c) != cap(c) {
        c <- []byte{}
    }
    log.Print("len(c) == cap(c)")
    time.Sleep(3 * chanMonitorInterval)
    <-c
    log.Print("len(c) < cap(c)")
    time.Sleep(5 * chanMonitorInterval)
    log.Print("main")
}

Playground: https://play.golang.org/p/c5VhIIO0pik

Output:

2009/11/10 23:00:00 main
2009/11/10 23:00:00 monitor
2009/11/10 23:00:05 len(c) == cap(c)
2009/11/10 23:00:06 log: len(10) cap(10)
2009/11/10 23:00:07 log: len(10) cap(10)
2009/11/10 23:00:08 log: len(10) cap(10)
2009/11/10 23:00:08 len(c) < cap(c)
2009/11/10 23:00:13 main

References:

Package reflect

The Go Blog: The Laws of Reflection

Upvotes: 3

Himanshu
Himanshu

Reputation: 12685

Create an interface{} type channel and pass any type wrapping around interface{}, then fetch the use type assert on receiving end.

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func monitorChan(ch chan interface{}) {
    val := <-ch
    fmt.Println(string(val.(interface{}).([]uint8)))
    wg.Done()
}

func main() {
    ch := make(chan interface{}, 100)
    wg.Add(1)
    ch <- []byte("hello")
    go monitorChan(ch)
    wg.Wait()
    // actual things below ...
}

Working code on Go Playground

Edited :- you can also go for reflect package to get the values of channels after wrapping the channels inside interface{}

package main

import (
    "fmt"
    "sync"
    "reflect"
)

var wg sync.WaitGroup

func monitorChan(i interface{}) {
    defer wg.Done()
    v := reflect.ValueOf(i)
    fmt.Printf("%s size: %d/%d\n", v.Kind(), v.Len(), v.Cap())
}

func main() {
    ch := make(chan []byte, 100)
    wg.Add(1)
    go monitorChan(ch)
    wg.Wait()
    // actual things below ...
}

Playground example

Upvotes: -1

Related Questions