github.com/golang-infrastructure/go-reflect-utils@v0.0.0-20221130143747-965ef2eb09c3/channel.go (about)

     1  package reflect_utils
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"unsafe"
     7  )
     8  
     9  // SafeClose 安全的关闭channel,如果已经被关闭的话会recover住panic
    10  // 不推荐使用此方法,一个好的结构清晰的代码不应该发生channel被重复关闭的情况,它只会在发送侧关闭一次才对
    11  func SafeClose[T any](channel chan T) bool {
    12  
    13  	if channel == nil {
    14  		return true
    15  	}
    16  
    17  	defer func() {
    18  		_ = recover()
    19  	}()
    20  
    21  	close(channel)
    22  	return true
    23  }
    24  
    25  // IsClosed 判断channel是否已经被关闭
    26  func IsClosed[T any](channel chan T) bool {
    27  
    28  	// 传入的参数非法的话就认为是已经关闭的channel
    29  	if channel == nil || reflect.TypeOf(channel).Kind() != reflect.Chan {
    30  		return true
    31  	}
    32  
    33  	// get interface value pointer, from cgo_export
    34  	// typedef struct { void *t; void *v; } GoInterface;
    35  	// then get channel real pointer
    36  	cptr := *(*uintptr)(unsafe.Pointer(
    37  		unsafe.Pointer(uintptr(unsafe.Pointer(&channel)) + unsafe.Sizeof(uint(0))),
    38  	))
    39  
    40  	// this function will return true if chan.closed > 0
    41  	// see hchan on https://github.com/golang/go/blob/master/src/runtime/chan.go
    42  	// type hchan struct {
    43  	// qcount   uint           // total data in the queue
    44  	// dataqsiz uint           // size of the circular queue
    45  	// buf      unsafe.Pointer // points to an array of dataqsiz elements
    46  	// elemsize uint16
    47  	// closed   uint32
    48  	// **
    49  
    50  	// qcount + dataqsiz
    51  	cptr += unsafe.Sizeof(uint(0)) * 2
    52  	// buf
    53  	cptr += unsafe.Sizeof(uintptr(0))
    54  	// elemsize
    55  	cptr += unsafe.Sizeof(uint16(0))
    56  	t := unsafe.Pointer(cptr)
    57  	fmt.Println(t)
    58  	return *(*uint32)(t) > 0
    59  }
    60  
    61