grim/gomain

Initial revision
draft default tip
2017-12-30, Gary Kramlich
4d8e9419a2eb
Parents
Children
Initial revision
  • +3 -0
    .hgignore
  • +22 -0
    README.md
  • +88 -0
    main.go
  • --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/.hgignore Sat Dec 30 01:40:11 2017 -0600
    @@ -0,0 +1,3 @@
    +syntax: glob
    +gomain
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/README.md Sat Dec 30 01:40:11 2017 -0600
    @@ -0,0 +1,22 @@
    +# gomain
    +
    +This is a simple example of how to call `os.Exit` in go while still allowing
    +deferred functions to run.
    +
    +This became necessary for me since I was writting a command line application
    +and needed to convey failures to the user via exit codes, but needed to make
    +sure all of my deferred functions ran.
    +
    +Per the [os.Exit() docs](https://golang.org/pkg/os/#Exit), defered functions
    +are not called. So this is a work around to make them be called while still
    +handling `panic`s.
    +
    +See the documentation in the code for more information on how this works.
    +
    +## Usage
    +
    +This application has two command line arguments for testing purposes:
    +
    + * `-x CODE` will cause the program to exit with exit code `CODE`
    + * `-p` will cause the program to panic
    +
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/main.go Sat Dec 30 01:40:11 2017 -0600
    @@ -0,0 +1,88 @@
    +// This is free and unencumbered software released into the public domain.
    +//
    +// Anyone is free to copy, modify, publish, use, compile, sell, or
    +// distribute this software, either in source code form or as a compiled
    +// binary, for any purpose, commercial or non-commercial, and by any
    +// means.
    +//
    +// In jurisdictions that recognize copyright laws, the author or authors
    +// of this software dedicate any and all copyright interest in the
    +// software to the public domain. We make this dedication for the benefit
    +// of the public at large and to the detriment of our heirs and
    +// successors. We intend this dedication to be an overt act of
    +// relinquishment in perpetuity of all present and future rights to this
    +// software under copyright law.
    +//
    +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    +// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
    +// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
    +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    +// OTHER DEALINGS IN THE SOFTWARE.
    +//
    +// For more information, please refer to <http://unlicense.org/>
    +
    +// gomain is a simple example of how to properly handle `os.Exit` in a go
    +// program. `os.Exit` exit's immediately and doesn't run any deferred
    +// functions. This can be very problematic when you need to do clean up
    +// but your application needs to be called by scripts.
    +//
    +// To work around this, we rename our normal `main` function to `gomain` and
    +// have it return an `int`. Then we write a new `main` function where all the
    +// magic happens. The magic is documented below.
    +//
    +// This program supports two command line arguments which are used for testing:
    +//
    +// * `-x CODE` will exit with an exit code of `CODE`. Defaults to `0`
    +// * `-p` will force a panic.
    +package main
    +
    +import (
    + "flag"
    + "fmt"
    + "os"
    + "runtime/debug"
    +)
    +
    +var (
    + exitCode = flag.Int("x", 0, "the exit code to return")
    + shouldPanic = flag.Bool("p", false, "whether or not to panic")
    +)
    +
    +// gomain is a simple application that exits based on some command line
    +// arguments.
    +func gomain() int {
    + flag.Parse()
    +
    + if *shouldPanic {
    + panic("panicking as requested")
    + }
    +
    + return *exitCode
    +}
    +
    +func main() {
    + // Set our default exit code
    + ec := 0
    +
    + // defer the function that'll call the actual os.Exit
    + defer func() {
    + // check if we `panic`'d. If we did, that means ec is still set to 0
    + // as `gomain` never returned, and so `ec` was never set below. So if
    + // we don't check the `panic` we'd just return an exit code of `0` and
    + // then pull our hair out trying to figure out what happened :)
    + if r := recover(); r != nil {
    + fmt.Printf("recovered panic: %#v\n%s", r, debug.Stack())
    + ec = 1
    + }
    +
    + // now that we're absolutely at the very end of deferred functions we
    + // can safely call `os.Exit` with our correct exit code.
    + os.Exit(ec)
    + }()
    +
    + // call the real main function and store it's result in `ec` which will
    + // eventually be passed to `os.Exit` as long as `gomain` doesn't `panic`.
    + ec = gomain()
    +}