Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CAFxX/ee9e09db6391656c954d636fc8e4c5e4 to your computer and use it in GitHub Desktop.
Save CAFxX/ee9e09db6391656c954d636fc8e4c5e4 to your computer and use it in GitHub Desktop.
golang: how to automatically release resources

Suppose you have a type that needs explicit destruction to release resources:

type MyType struct {
  ...
}

func New() *MyType {
  // acquire resources...
  
  return &MyType{ ... }
}

// other (m* MyType) functions...

func (m *MyType) Close() {
  // release resources
}

If the users of your library forget to call Close() the resources won't be freed until the process terminates.

With some changes it is possible to add to MyType the ability to automatically release the resources during GC. This can be done transparently without requiring changes to the users of your type.

Step 1: turn MyType into a handle

Rename your type (MyType -> innerType) and embed it in a new type with the same name as the original type (MyType).

This way MyType becomes an opaque handle for the real struct (now called innerType).

type MyType struct {
  *innerType
}

type innerType struct {
  ...
}

func New() *MyType {
  // acquire resources...
  
  return &MyType{ &innerType{ ... } }
}

// other (m* innerType) functions...

func (m *innerType) Close() {
  // release resources
}

If this was done correctly, your implementation should still work correctly and users in other packages should not be affected.

Step 2: detect when the handle becomes unreachable

Create the following type that will be used to detect when the handle becomes unreachable. To avoid interations between finalizers and tiny allocations (<16 bytes) make sure the type size is at least 16 bytes.

type unreachableGuard [16]byte

Modify the handle type to include a pointer to the guard:

type MyType struct {
  *innerType
  *unreachableGuard
}

Modify the constructor to create the guard:

func New() *MyType {
  // acquire resources...
  
  return &MyType{ &innerType{ ... }, new(unreachableGuard) }
}

Create a finalizer that will release the resources when the handle becomes unreachable:

func New() *MyType {
  // acquire resources...
  
  i := &innerType{ ... }
  m := &MyType{ i, new(unreachableGuard) }
  
  runtime.SetFinalizer(m.unreachableGuard, func (_ *unreachableGuard) {
    i.Close()
  })
  
  return m
}

step 3: add tests

Add tests to ensure that the resources are getting automatically released:

func TestAutomaticRelease(t *testing.T) {
  m := New()
  i := m.innerType
  // m is now unreacheable
  runtime.GC()
  // assume i.resource is set to nil when Close() is called
  for i.resource != nil {
    runtime.Gosched()
  }
}
@ArdeshirV
Copy link

Thank you!
You are super genius!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment