grim/gomain
Clone
Summary
Browse
Changes
Graph
Initial revision
draft
default
tip
2017-12-30, Gary Kramlich
4d8e9419a2eb
Parents
Children
Initial revision
3 files changed, 113 insertions(+), 0 deletions(-)
+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()
+}