For example, several fatal scenes in Go language that cannot be recovered

Error type

error

The first is the most standard error in Go, which is actually an interface {}.

As follows:

type error interface {
    Error() string
}

In daily engineering, we only need to create any structure and implement the error method, which can be regarded as the error error type.

As follows:

type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

Call the standard library API externally, generally as follows:

f, err := os.Open("filename.ext")
if err != nil {
    log.Fatal(err)
}
// do something with the open *File f

We will agree that the last parameter is of error type, which is generally common in the second parameter. We can have a conventional habit.

panic

The second is the panic exception handling in Go, which can generate abnormal errors. Combined with panic+recover, the running state of the program can be reversed.

As follows:

package main

import "os"

func main() {
    panic("a problem")

    _, err := os.Create("/tmp/file")
    if err != nil {
        panic(err)
    }
}

Output results:

$ go run panic.go
panic: a problem
goroutine 1 [running]:
main.main()
    /.../panic.go:12 +0x47
...
exit status 2

If recover is not used as a capture, the program will be interrupted. Therefore, it is often mistaken for program interruption, which is 100% caused by panic.

This is a misunderstanding.

throw

The third type of error that Go beginners often tread on and don't know is the fatal error throw.

This error type cannot be called actively on the user side. It is called by the Go bottom layer. For example, it is triggered by the common map concurrent reading and writing.

Its source code is as follows:

func throw(s string) {
 systemstack(func() {
  print("fatal error: ", s, "\n")
 })
 gp := getg()
 if gp.m.throwing == 0 {
  gp.m.throwing = 1
 }
 fatalthrow()
 *(*int)(nil) = 0 // not reached
}

According to the above procedure, the current instance of G will be obtained and the throwing state of M will be set to 1.

After the status is set, the {fatalthrow} method will be called for real crash related operations:

func fatalthrow() {
 pc := getcallerpc()
 sp := getcallersp()
 gp := getg()
 
 systemstack(func() {
  startpanic_m()
  if dopanic_m(gp, pc, sp) {
   crash()
  }

  exit(2)
 })

 *(*int)(nil) = 0 // not reached
}

The body logic is send_ The SIGABRT semaphore is finally called out by the exit method, so you will find that this is a fatal mistake.

Fatal scene

Therefore, as a "mature" Go engineer, in addition to ensuring the robustness of my program, I also collected some fatal error scenarios on the Internet and shared them with you.

Learn and avoid these fatal scenes together, strive to get an A at the end of the year, and don't carry P0 accidents.

Concurrent read / write map

func foo() {
 m := map[string]int{}
 go func() {
  for {
   m["Fried fish 1"] = 1
  }
 }()
 for {
  _ = m["Fried fish 2"]
 }
}

Output results:

fatal error: concurrent map read and map write

goroutine 1 [running]:
runtime.throw(0x1078103, 0x21)
...

Stack memory exhausted

func foo() {
 var f func(a [1000]int64)
 f = func(a [1000]int64) {
  f(a)
 }
 f([1000]int64{})
}

Output results:

runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e1bf0 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow

runtime stack:
runtime.throw(0x1074ba3, 0xe)
        /usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:1117 +0x72
runtime.newstack()
...

Start the nil function as goroutine

func foo() {
 var f func()
 go f()
}

Output results:

fatal error: go of nil func value

goroutine 1 [running]:
main.foo()
...

Keywords: Go

Added by OuchMedia on Mon, 03 Jan 2022 15:58:55 +0200