How to wait for threads with low latency in go? - mutex

I've been trying to create a simple event loop wrapper in Go. But I got stumped, how was I supposed to keep track of operations in the current thread?
I wanted CurrentTick to run a function, and even if the calling function quits, not start the next tick until all functions run by CurrentTick quit. I thought I might use a mutex to monitor the number of threads, but I realized if I kept checking that over and over it would throttle the CPU. If I used time.Sleep it would be latent. How would you solve the problem?
package eventloop
import (
"reflect"
)
type eventLoop *struct{
functions []reflect.Value
addFunc chan<-/*3*/ reflect.Value
mutex chan/*1*/ bool
threads int
}
func NewEventLoop() eventLoop {
var funcs chan reflect.Value
loop := eventLoop{
[]Reflect.Value{},
funcs = make(chan reflect.Value, 3),
make(chan bool, 1),
0,
}
go func(){
for {
this.mutex <- 1
if threads == 0 {
}
}
}
}
func (this eventLoop) NextTick(f func()) {
this.addFunc <- reflect.ValueOf(f)
}
func (this eventLoop) CurrentTick(f func()) {
this.mutex <- 1
threads += 1
<-this.mutex
go func() {
f()
this.mutex <- 1
threads -= 1
<-this.mutex
}()
}

If I understand your intent, I think you're overcomplicating things. I'd do it like this:
package eventloop
type EventLoop struct {
nextFunc chan func()
curFunc chan func()
}
func NewEventLoop() *EventLoop {
el := &EventLoop{
// Adjust the capacities to taste
make(chan func(), 3),
make(chan func(), 3),
}
go eventLoop(el)
return el
}
func (el *EventLoop) NextTick(f func()) {
el.nextFunc <- f
}
func (el *EventLoop) CurrentTick(f func()) {
el.curFunc <- f
}
func (el *EventLoop) Quit() {
close(el.nextFunc)
}
func eventLoop(el *EventLoop) {
for {
f, ok := <-el.nextFunc
if !ok {
return
}
f()
drain: for {
select {
case f := <-el.curFunc:
f()
default:
break drain
}
}
}
}
Depending on your use, you may need to add some synchronization to make sure all tasks in the loop finish before your program exits.

I figured it out myself, after a lot of problems and random issues including using 15 as length instead of capacity... Seems you just have a thread send a message after you decrement the counter. (the loop.tick part could be inlined, but I'm not worried about that)
package eventloop
type eventLoop struct{
functions []func()
addFunc chan/*3*/ func()
mutex chan/*1*/ bool
threads int
waitChannel chan bool
pauseState chan bool
}
func (this *eventLoop) NextTick (f func()) {
this.addFunc <- f
}
func (this *eventLoop) tick () {
this.mutex <- true
for this.threads != 0 {
<-this.mutex
<-this.waitChannel
this.mutex <- true
}
<-this.mutex
L1: for {
select {
case f := <-this.addFunc:
this.functions = append(this.functions,f)
default: break L1
}
}
if len(this.functions) != 0 {
this.functions[0]()
if len(this.functions) >= 2 {
this.functions = this.functions[1:]
} else {
this.functions = []func(){}
}
} else {
(<-this.addFunc)()
}
}
func (this *eventLoop) CurrentTick (f func()) {
this.mutex <- true
this.threads += 1
<-this.mutex
go func() {
f()
this.mutex <- true
this.threads -= 1
<-this.mutex
this.waitChannel <- true
}()
}
func NewEventLoop () *eventLoop {
funcs := make(chan func(),3)
loop := &eventLoop{
make([]func(),0,15), /*functions*/
funcs, /*addFunc*/
make(chan bool, 1), /*mutex for threads*/
0, /*Number of threads*/
make(chan bool,0), /*The "wait" channel*/
make(chan bool,1),
}
go func(){
for { loop.tick() }
}()
return loop
}
Note: this still has lots of other problems.

Related

Question about the conditional sentence of Observable. (RxSwift)

I tried to create a function runsample() that uses multiple observables as below.
If I meet a specific condition in the middle of the stream, I want to start from the beginning of function.
(foo1() in the example below)
In this case, how do I modify the runsample() function?
class SampleClass {
////////////////////////////////
// private
////////////////////////////////
private func foo1() -> Observable<String> {
// Do something
return .just("TEST")
}
private func foo2() -> Observable<Bool> {
// Do something
return .just(false) // or true
}
private func foo3() -> Observable<String> {
// Do something
return .just("Result")
}
////////////////////////////////
// public
////////////////////////////////
public func runSample() -> Observable<String> {
return Observable.just(())
.flatMap { [unowned self] _ in
self.foo1()
}
.flatMap { [unowned self] _ in
self.foo2()
}
// I want to retry foo1() when foo2() is false
// I want to make foo3() run only if foo2() is true.
.flatMap { [unowned self] _ in
self.foo3()
}
}
}
Based on your comment, this is what you want:
func runSample() -> Observable<String> {
struct NotValid: Error { }
return Observable.deferred {
foo1().flatMap { _ in
foo2().do(onNext: { isValid in
if !isValid { throw NotValid() }
})
}
}
.retry()
.flatMap { _ in foo3() }
}
It's a very strange requirement you have, but it's doable. I expect this is an X-Y problem though.
You really want to retry foo1()? That would imply that it failed but it obviously didn't. In any case, this will do what you want:
func runSample() -> Observable<String> {
foo1()
.flatMap { [foo2] _ in
foo2()
}
.flatMap { [foo1, foo3] isTrue in
isTrue ? foo3() : foo1()
}
}
This function will return an Observable. Every time that Observable is subscribed to, the first foo1() will be activated.
Every time the first foo1() emits a value, the value will be ignored (which is quite odd) and foo2() will be called. This will generate a new Observable which will be subscribed to.
Whenever any of the Observables generated by foo2() emit a value, if the value is true foo3() will be called, otherwise foo1() will be called. Whichever one is called, its Observable will be subscribed to.
The entire function will emit all the values that any foo1()s or foo3()s Observables emit.
Importantly for this example, you do not need to start with Observable.just(()).
Thinking about it, I'd prefer something like this:
func runSample() -> Observable<String> {
Observable.zip(foo1(), foo2())
.flatMap { $0.1 ? foo3() : .just($0.0) }
}
That way I don't have to run foo1() twice.

Temporary lock the resource until goroutine is finished

I have a method, which sends HTTP status-code 202 Accepted as indicator of successful request, and runs another process in it (goroutine). Something like that:
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
go func() {
time.Sleep(2 * time.Second)
}()
}
I want to temporarily lock the resource to prevent multiple execution of goroutine process.
return func(w http.ResponseWriter, r *http.Request) {
c := make(chan bool)
select {
case _, unlocked := <-c:
if unlocked {
break
}
default:
w.WriteHeader(http.StatusLocked)
return
}
w.WriteHeader(http.StatusAccepted)
go func(c chan bool) {
time.Sleep(2 * time.Second)
c <- true
}(c)
}
I'm get 423 Locked status code always. I think, I don't understand channel yet. May be try to use mutexes?
Not the best solution, but it works:
func (h *HookHandler) NewEvents() http.HandlerFunc {
eventsLocker := struct {
v bool
mux *sync.Mutex
}{
v: true,
mux: &sync.Mutex{},
}
return func(w http.ResponseWriter, r *http.Request) {
if !eventsLocker.v {
w.WriteHeader(http.StatusLocked)
return
}
w.WriteHeader(http.StatusAccepted)
go func() {
defer eventsLocker.mux.Unlock()
defer func() { eventsLocker.v = true }()
eventsLocker.mux.Lock()
eventsLocker.v = false
time.Sleep(2 * time.Second)
}()
}
}

Go Golang select statement cannot receive sended value

I'm new to Go and trying to implement a simple load balancer as seen in this slides:
http://concur.rspace.googlecode.com/hg/talk/concur.html#slide-42
The complete code:
package main
import (
"fmt"
"time"
"container/heap"
)
type Request struct {
fn func(*Worker) int
c chan int
}
func requester(work chan <-Request) {
c := make(chan int)
work <- Request{workFn, c}
result := <-c
furtherProcess(result)
}
func workFn(w *Worker) int {
time.Sleep(1000 * time.Millisecond)
return w.index
}
func furtherProcess(result int) {
fmt.Println(result)
}
type Worker struct {
request chan Request
pending int
index int
}
func (w *Worker) work(done chan *Worker) {
for req := range w.request {
req.c <- req.fn(w)
fmt.Println("sending to done:", done)
done <- w
fmt.Println("sended to done")
}
}
type Pool []*Worker
type Balancer struct {
pool Pool
done chan *Worker
}
func (b *Balancer) balance(work chan Request) {
for {
fmt.Println("selecting, done:", b.done)
select {
case req := <-work:
b.dispatch(req)
case w := <-b.done:
fmt.Println("completed")
b.completed(w)
}
}
}
func (p Pool) Len() int {
return len(p)
}
func (p Pool) Less(i, j int) bool {
return p[i].pending < p[j].pending
}
func (p Pool) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
func (p *Pool) Push(x interface{}) {
*p = append(*p, x.(*Worker))
}
func (p *Pool) Pop() interface{} {
old := *p
n := len(old)
x := old[n - 1]
*p = old[0 : n - 1]
return x
}
func (b *Balancer) dispatch(req Request) {
w := heap.Pop(&b.pool).(*Worker)
w.request <- req
w.pending++
heap.Push(&b.pool, w)
fmt.Println("dispatched to worker", w.index)
}
func (b *Balancer) completed(w *Worker) {
w.pending--
heap.Remove(&b.pool, w.index)
heap.Push(&b.pool, w)
}
func Run() {
NumWorkers := 4
req := make(chan Request)
done := make(chan *Worker)
b := Balancer{make([]*Worker, NumWorkers), done}
for i := 0; i < NumWorkers; i++ {
w := Worker{make(chan Request), 0, i}
b.pool[i] = &w
go w.work(done)
}
go b.balance(req)
for i := 0; i < NumWorkers * 4; i++ {
go requester(req)
}
time.Sleep(200000 * time.Millisecond)
}
func main() {
Run()
}
When I ran it, I got following outputs:
selecting, done: 0xc0820082a0
dispatched to worker 0
selecting, done: 0xc0820082a0
dispatched to worker 3
selecting, done: 0xc0820082a0
dispatched to worker 2
selecting, done: 0xc0820082a0
dispatched to worker 1
selecting, done: 0xc0820082a0
sending to done: 0xc0820082a0
sending to done: 0xc0820082a0
3
sending to done: 0xc0820082a0
2
1
0
sending to done: 0xc0820082a0
As you can see, it was selecting on and sending to the same pipe (done: 0xc0820082a0), but the select didn't receive the sended value and was blocking forever. How could this happen? What's the problem with the above code? Thanks!
Using kill -ABRT <PID> you can see that all your Workers are blocked on done <- w while your Balancer is blocked on w.request <- req, creating a deadlock (workers can't go further until the balancer receives their "done" signals, and the balancer can't go further until the selected worker takes the request).
If you replace done <- w by go func() { done <- w }(), you can see that your program will process the 16 requests without hanging.
Side note: instead of time.Sleep(200000 * time.Millisecond), look into sync.WaitGroup

Accessing the underlying socket of a net/http response

I'm new to Go and evaluating it for a project.
I'm trying to write a custom handler to serve files with net/http.
I can't use the default http.FileServer() handler because I need to have access to the underlying socket (the internal net.Conn) so I can perform some informational platform specific "syscall" calls on it (mainly TCP_INFO).
More precisly: I need to access the underlying socket of the http.ResponseWriter in the handler function:
func myHandler(w http.ResponseWriter, r *http.Request) {
...
// I need the net.Conn of w
...
}
used in
http.HandleFunc("/", myHandler)
Is there a way to this. I looked at how websocket.Upgrade does this but it uses Hijack() which is 'too much' because then I have to code 'speaking http' over the raw tcp socket I get. I just want a reference to the socket and not taking over completely.
After Issue #30694 is completed, it looks like Go 1.13 will probably support storing the net.Conn in the Request Context, which makes this fairly clean and simple:
package main
import (
"net/http"
"context"
"net"
"log"
)
type contextKey struct {
key string
}
var ConnContextKey = &contextKey{"http-conn"}
func SaveConnInContext(ctx context.Context, c net.Conn) (context.Context) {
return context.WithValue(ctx, ConnContextKey, c)
}
func GetConn(r *http.Request) (net.Conn) {
return r.Context().Value(ConnContextKey).(net.Conn)
}
func main() {
http.HandleFunc("/", myHandler)
server := http.Server{
Addr: ":8080",
ConnContext: SaveConnInContext,
}
server.ListenAndServe()
}
func myHandler(w http.ResponseWriter, r *http.Request) {
conn := GetConn(r)
...
}
Until then ... For a server listening on a TCP port, net.Conn.RemoteAddr().String() is unique for each connection and is available to the http.Handler as r.RemoteAddr, so it can be used as a key to a global map of Conns:
package main
import (
"net/http"
"net"
"fmt"
"log"
)
var conns = make(map[string]net.Conn)
func ConnStateEvent(conn net.Conn, event http.ConnState) {
if event == http.StateActive {
conns[conn.RemoteAddr().String()] = conn
} else if event == http.StateHijacked || event == http.StateClosed {
delete(conns, conn.RemoteAddr().String())
}
}
func GetConn(r *http.Request) (net.Conn) {
return conns[r.RemoteAddr]
}
func main() {
http.HandleFunc("/", myHandler)
server := http.Server{
Addr: ":8080",
ConnState: ConnStateEvent,
}
server.ListenAndServe()
}
func myHandler(w http.ResponseWriter, r *http.Request) {
conn := GetConn(r)
...
}
For a server listening on a UNIX socket, net.Conn.RemoteAddr().String() is always "#", so the above doesn't work. To make this work, we can override net.Listener.Accept(), and use that to override net.Conn.RemoteAddr().String() so that it returns a unique string for each connection:
package main
import (
"net/http"
"net"
"os"
"golang.org/x/sys/unix"
"fmt"
"log"
)
func main() {
http.HandleFunc("/", myHandler)
listenPath := "/var/run/go_server.sock"
l, err := NewUnixListener(listenPath)
if err != nil {
log.Fatal(err)
}
defer os.Remove(listenPath)
server := http.Server{
ConnState: ConnStateEvent,
}
server.Serve(NewConnSaveListener(l))
}
func myHandler(w http.ResponseWriter, r *http.Request) {
conn := GetConn(r)
if unixConn, isUnix := conn.(*net.UnixConn); isUnix {
f, _ := unixConn.File()
pcred, _ := unix.GetsockoptUcred(int(f.Fd()), unix.SOL_SOCKET, unix.SO_PEERCRED)
f.Close()
log.Printf("Remote UID: %d", pcred.Uid)
}
}
var conns = make(map[string]net.Conn)
type connSaveListener struct {
net.Listener
}
func NewConnSaveListener(wrap net.Listener) (net.Listener) {
return connSaveListener{wrap}
}
func (self connSaveListener) Accept() (net.Conn, error) {
conn, err := self.Listener.Accept()
ptrStr := fmt.Sprintf("%d", &conn)
conns[ptrStr] = conn
return remoteAddrPtrConn{conn, ptrStr}, err
}
func GetConn(r *http.Request) (net.Conn) {
return conns[r.RemoteAddr]
}
func ConnStateEvent(conn net.Conn, event http.ConnState) {
if event == http.StateHijacked || event == http.StateClosed {
delete(conns, conn.RemoteAddr().String())
}
}
type remoteAddrPtrConn struct {
net.Conn
ptrStr string
}
func (self remoteAddrPtrConn) RemoteAddr() (net.Addr) {
return remoteAddrPtr{self.ptrStr}
}
type remoteAddrPtr struct {
ptrStr string
}
func (remoteAddrPtr) Network() (string) {
return ""
}
func (self remoteAddrPtr) String() (string) {
return self.ptrStr
}
func NewUnixListener(path string) (net.Listener, error) {
if err := unix.Unlink(path); err != nil && !os.IsNotExist(err) {
return nil, err
}
mask := unix.Umask(0777)
defer unix.Umask(mask)
l, err := net.Listen("unix", path)
if err != nil {
return nil, err
}
if err := os.Chmod(path, 0660); err != nil {
l.Close()
return nil, err
}
return l, nil
}
Note that although in current implementation http.ResponseWriter is a *http.response (note the lowercase!) which holds the connection, the field is unexported and you can't access it.
Instead take a look at the Server.ConnState hook: you can "register" a function which will be called when the connection state changes, see http.ConnState for details. For example you will get the net.Conn even before the request enters the handler (http.StateNew and http.StateActive states).
You can install a connection state listener by creating a custom Server like this:
func main() {
http.HandleFunc("/", myHandler)
s := &http.Server{
Addr: ":8081",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
ConnState: ConnStateListener,
}
panic(s.ListenAndServe())
}
func ConnStateListener(c net.Conn, cs http.ConnState) {
fmt.Printf("CONN STATE: %v, %v\n", cs, c)
}
This way you will have exactly the desired net.Conn even before (and also during and after) invoking the handler. The downside is that it is not "paired" with the ResponseWriter, you have to do that manually if you need that.
You can use an HttpHijacker to take over the TCP connection from the ResponseWriter. Once you've done that you're free to use the socket to do whatever you want.
See http://golang.org/pkg/net/http/#Hijacker, which also contains a good example.
This can be done with reflection. it's a bit "dirty" but it works:
package main
import "net/http"
import "fmt"
import "runtime"
import "reflect"
func myHandler(w http.ResponseWriter, r *http.Request) {
ptrVal := reflect.ValueOf(w)
val := reflect.Indirect(ptrVal)
// w is a "http.response" struct from which we get the 'conn' field
valconn := val.FieldByName("conn")
val1 := reflect.Indirect(valconn)
// which is a http.conn from which we get the 'rwc' field
ptrRwc := val1.FieldByName("rwc").Elem()
rwc := reflect.Indirect(ptrRwc)
// which is net.TCPConn from which we get the embedded conn
val1conn := rwc.FieldByName("conn")
val2 := reflect.Indirect(val1conn)
// which is a net.conn from which we get the 'fd' field
fdmember := val2.FieldByName("fd")
val3 := reflect.Indirect(fdmember)
// which is a netFD from which we get the 'sysfd' field
netFdPtr := val3.FieldByName("sysfd")
fmt.Printf("netFDPtr= %v\n", netFdPtr)
// which is the system socket (type is plateform specific - Int for linux)
if runtime.GOOS == "linux" {
fd := int(netFdPtr.Int())
fmt.Printf("fd = %v\n", fd)
// fd is the socket - we can call unix.Syscall6(unix.SYS_GETSOCKOPT, uintptr(fd),....) on it for instance
}
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", myHandler)
err := http.ListenAndServe(":8081", nil)
fmt.Println(err.Error())
}
Ideally the library should be augmented with a method to get the underlying net.Conn
Expanding on KGJV's answer, a working solution using reflection to maintain a map of connections indexed by net.Conn instance memory addresses.
Instances of net.Conn can be looked up by pointer, and pointers derived using reflection against http.Response.
It's a bit nasty, but given you can't access unpublished fields with reflection it's the only way I could see of doing it.
// Connection array indexed by connection address
var conns = make(map[uintptr]net.Conn)
var connMutex = sync.Mutex{}
// writerToConnPrt converts an http.ResponseWriter to a pointer for indexing
func writerToConnPtr(w http.ResponseWriter) uintptr {
ptrVal := reflect.ValueOf(w)
val := reflect.Indirect(ptrVal)
// http.conn
valconn := val.FieldByName("conn")
val1 := reflect.Indirect(valconn)
// net.TCPConn
ptrRwc := val1.FieldByName("rwc").Elem()
rwc := reflect.Indirect(ptrRwc)
// net.Conn
val1conn := rwc.FieldByName("conn")
val2 := reflect.Indirect(val1conn)
return val2.Addr().Pointer()
}
// connToPtr converts a net.Conn into a pointer for indexing
func connToPtr(c net.Conn) uintptr {
ptrVal := reflect.ValueOf(c)
return ptrVal.Pointer()
}
// ConnStateListener bound to server and maintains a list of connections by pointer
func ConnStateListener(c net.Conn, cs http.ConnState) {
connPtr := connToPtr(c)
connMutex.Lock()
defer connMutex.Unlock()
switch cs {
case http.StateNew:
log.Printf("CONN Opened: 0x%x\n", connPtr)
conns[connPtr] = c
case http.StateClosed:
log.Printf("CONN Closed: 0x%x\n", connPtr)
delete(conns, connPtr)
}
}
func HandleRequest(w http.ResponseWriter, r *http.Request) {
connPtr := writerToConnPtr(w)
connMutex.Lock()
defer connMutex.Unlock()
// Requests can access connections by pointer from the responseWriter object
conn, ok := conns[connPtr]
if !ok {
log.Printf("error: no matching connection found")
return
}
// Do something with connection here...
}
// Bind with http.Server.ConnState = ConnStateListener
It looks like you cannot "pair" a socket (or net.Conn) to either http.Request or http.ResponseWriter.
But you can implement your own Listener:
package main
import (
"fmt"
"net"
"net/http"
"time"
"log"
)
func main() {
// init http server
m := &MyHandler{}
s := &http.Server{
Handler: m,
}
// create custom listener
nl, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
l := &MyListener{nl}
// serve through custom listener
err = s.Serve(l)
if err != nil {
log.Fatal(err)
}
}
// net.Conn
type MyConn struct {
nc net.Conn
}
func (c MyConn) Read(b []byte) (n int, err error) {
return c.nc.Read(b)
}
func (c MyConn) Write(b []byte) (n int, err error) {
return c.nc.Write(b)
}
func (c MyConn) Close() error {
return c.nc.Close()
}
func (c MyConn) LocalAddr() net.Addr {
return c.nc.LocalAddr()
}
func (c MyConn) RemoteAddr() net.Addr {
return c.nc.RemoteAddr()
}
func (c MyConn) SetDeadline(t time.Time) error {
return c.nc.SetDeadline(t)
}
func (c MyConn) SetReadDeadline(t time.Time) error {
return c.nc.SetReadDeadline(t)
}
func (c MyConn) SetWriteDeadline(t time.Time) error {
return c.nc.SetWriteDeadline(t)
}
// net.Listener
type MyListener struct {
nl net.Listener
}
func (l MyListener) Accept() (c net.Conn, err error) {
nc, err := l.nl.Accept()
if err != nil {
return nil, err
}
return MyConn{nc}, nil
}
func (l MyListener) Close() error {
return l.nl.Close()
}
func (l MyListener) Addr() net.Addr {
return l.nl.Addr()
}
// http.Handler
type MyHandler struct {
// ...
}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}

Explanation of checking if value implements interface

I've read "Effective Go" and other Q&As like this: golang interface compliance compile type check , but nonetheless I can't understand properly how to use this technique.
Please, see example:
type Somether interface {
Method() bool
}
type MyType string
func (mt MyType) Method2() bool {
return true
}
func main() {
val := MyType("hello")
//here I want to get bool if my value implements Somether
_, ok := val.(Somether)
//but val must be interface, hm..what if I want explicit type?
//yes, here is another method:
var _ Iface = (*MyType)(nil)
//but it throws compile error
//it would be great if someone explain the notation above, looks weird
}
Is there any simple ways (eg without using reflection) check value if it implements an interface?
You only have to check if a value implements an interface if you don't know the value's type.
If the type is known, that check is automatically done by the compiler.
If you really want to check anyways, you can do it with the second method you gave:
var _ Somether = (*MyType)(nil)
which would error at compile time:
prog.go:23: cannot use (*MyType)(nil) (type *MyType) as type Somether in assignment:
*MyType does not implement Somether (missing Method method)
[process exited with non-zero status]
What you are doing here, is assigning a pointer of MyType type (and nil value) to a variable of type Somether, but since the variable name is _ it is disregarded.
If MyType implemented Somether, it would compile and do nothing
Following will work:
val:=MyType("hello")
var i interface{}=val
v, ok:=i.(Somether)
It is also possible to use Implements(u Type) bool method of reflect.Type in the following way:
package main
import (
"reflect"
)
type Somether interface {
Method() bool
}
type MyType string
func (mt MyType) Method() bool {
return true
}
func main() {
inter := reflect.TypeOf((*Somether)(nil)).Elem()
if reflect.TypeOf(MyType("")).Implements(inter) {
print("implements")
} else {
print("doesn't")
}
}
You can read more on that in the documentation.
You can also take Alpha's solution:
val := MyType("hello")
var i interface{} = val
v, ok := i.(Somether)
... and reduce it further:
val := MyType("hello")
v, ok := interface{}(val).(Somether)
If you're trying to test for one-off methods, you can even do something like:
val := MyType("hello")
v, ok := interface{}(val).(interface {
Method() bool
})
NOTE: Make sure you are very careful with "pointer receiver" versus "value receiver" implementations. If the implementation uses a pointer the assertion will fail when passing in a value object. If the implementation uses a value receiver, both assertions will pass.
// Implement Somether as a POINTER receiver method
func (mt *MyType) Method() bool {
return true
}
func main() {
val := MyType("hello")
v, ok := interface{}(val).(Somether)
fmt.Println(v, ok)
// Output: <nil> false
// Notice the pass by reference
v, ok := interface{}(&val).(Somether)
fmt.Println(v, ok)
// Output: 0xc000010200 true
}
versus
// Implement Somether as a VALUE receiver method
func (mt MyType) Method() bool {
return true
}
func main() {
val := MyType("hello")
v, ok := interface{}(val).(Somether)
fmt.Println(v, ok)
// Output: hello true
// Notice the pass by reference
v, ok := interface{}(&val).(Somether)
fmt.Println(v, ok)
// Output: 0xc00008e1e0 true
}
I have a solution that I use to complement a panic handler pattern
I have not exhaustively tested the code but casual testing is affirmative
I welcome any suggestions to improve or other go expert stuff
// -------------------------------------------------------------- //
// hasErrIface -
// ---------------------------------------------------------------//
func hasErrIface(v reflect.Value) (error, bool) {
// CanInterface reports whether Interface can be used without panicking
if !v.CanInterface() {
return nil, false
}
// Interface panics if the Value was obtained by accessing unexported struct fields
err, ok := v.Interface().(error)
return err, ok
}
// -------------------------------------------------------------- //
// HasErrKind
// ---------------------------------------------------------------//
func HasErrKind(r interface{}) (err error, isErr bool) {
err = nil
isErr = false
v := reflect.ValueOf(r)
switch v.Kind() {
case reflect.Struct:
errtype := reflect.TypeOf((*error)(nil)).Elem()
if v.Type().Implements(errtype) {
err, isErr = v.Interface().(error)
}
case reflect.Ptr:
err, isErr = hasErrIface(v)
case reflect.Interface:
err, isErr = hasErrIface(v)
}
return
}
// -------------------------------------------------------------- //
// EvalErrKind
// ---------------------------------------------------------------//
func EvalErrKind(r interface{}) (errval error) {
err, isErr := HasErrKind(r)
if !isErr {
errtxt := "Unknown system error - %v :\n%v"
v := reflect.ValueOf(r)
return fmt.Errorf(errtxt, v.Type(), v)
}
return err
}
// -------------------------------------------------------------- //
// PanicHandler
// ---------------------------------------------------------------//
func PanicHandler(errHandler func(error)) func() {
return func() {
var err error
if r := recover(); r != nil {
switch r.(type) {
case ApiError:
err = r.(ApiError)
default:
err = EvalErrKind(r)
}
if errHandler != nil {
errHandler(err)
}
}
}
}
It only works with Go 1.18 or higher
//implements interface
func implInter[T any](obj any) bool {
⠀ _, ok := obj.(T)
return ok
}
var obj any = "some text"
if impleInter[string](obj) {
⠀ newObj, _ := obj.(string)
}
You can also use this:
If MyType implements the Somether interface, this should compile
// use new to create a pointer to type MyType
var _ Somether = new(MyType)
var _ Somether = MyType("")