Freeing up resources in GO

Go is a language with automatic memory management: the garbage collector, without the need for a programmer, takes care of reclaiming the memory occupied by objects no longer used by the program. But all automation in general is limited by memory - we still need to take care of the rest of the resources used by the program ourselves.





Image source: http://kenimation.net/doctor-who%E2%80%AC-dalek/
Image source: http://kenimation.net/doctor-who%E2%80%AC-dalek/

, - , runtime.SetFinalizer. , , . Go - , , , , .





, - . , , - , .





res1, err := NewResource1()
if err != nil {
    return nil, err
}

res2, err := NewResource2(res1)
if err != nil {
  res1.Close()
  return nil, err
}

res3, err := NewResource3(res2)
if err != nil {
  res2.Close()
  res1.Close()
  return nil, err
}

v, err := res3.DoSomething()
if err != nil {
  res3.Close()
  res2.Close()
  res1.Close()
  return nil, err
}

res3.Close()
res2.Close()
res1.Close()
return v, nil
      
      



, . , ( ). Close , - .





. , C# Java using statement try-with-resources statement . Go , , defer statement. , :





res1, err := NewResource1()
if err != nil {
  return nil, err
}
defer res1.Close()

res2, err := NewResource2(res1)
if err != nil {
  return nil, err
}
defer res2.Close()

res3, err := NewResource3(res2)
if err != nil {
  return nil, err
}
defer res3.Close()

return res3.DoSomething()
      
      



Close . Close - .





, . - defer . , , - , . , , . , , - , - - , (, main), , , , , .





, Wire. ( Wire) . . cleanup function, . .





Dedicated finalization

, c cleanup function, Wire, . , Close ( ) :





  • ;





  • .





, , , . , Go :





res, cleanup, err := NewResource()
if err != nil {
  return err
}
//    cleanup,     .

if err := res.DoSomething(); err != nil {
  return err
}
      
      



, (, ) ( ) . , " ", , , "" "" , .





Composite finalization

defer (, ), :





func Finalize(finalizers ...func()) {
  //     .
  for i := len(finalizers) - 1; i >= 0; i-- {
    func() {
      defer func() {
        //      ,    .
        //        multierror :
        // 1)    ;
        // 2)    .
        recover()
      }()
      finalizers[i]()
    }()
  }
}

func NewResource3() (*Resource3, func(), error) {
  var (
    finalizers []func() //    
    successful bool     //    
  )
  defer func() {
    //       ,
    //        -
    //       .
    if !successfull {
      Finalize(finalizers...)
    }
  }()
  
  res1, fin1, err := NewResource1()
  if err != nil {
    return nil, nil, err
  }
  finalizers = append(finalizers, fin1)
  
  res2, fin2, err := NewResource2(res1)
  if err != nil {
    return nil, nil, err
  } 
  finalizers = append(finalizers, fin2)
  
  res3 := &Resource3{
    resource2: res2,
  }
  fin3 := func() {
    Finalize(finalizers...)
  }
  
  //        .
  //        
  //     .
  successful = true
  return res3, fin3, nil
}
      
      



Finalize - .





new - error - defer , , , , .





KDone

I have posted the KDone library providing the above set of tools. She is part of the Kata project , which will be discussed next. We can assume that at the moment its API is stable and if it changes, it will be insignificant - nevertheless, the library is still fresh and I still use the zero major version in case of unforeseen changes.





A typical constructor using this library looks like this:





func NewResource(...) (res Resource, dtor kdone.Destructor, err error) {
  
  defer kerror.Catch(&err)               //    
                                         //   KError.
                                         //    .
  
  reaper := kdone.NewReaper()            //  reaper.
  defer reaper.MustFinalize()            //   
                                         //   .
  
  // ... reaper.MustAssume(dtor) ...     //   reaper 
                                         //    
                                         // .
  
  return res, reaper.MustRelease(), nil  //  reaper  
                                         //      
                                         //  .
}
      
      



What do you think? The concept is simple enough, but maybe I missed something in my reasoning? Or do you have suggestions for improvements? I would be glad to have discussions in the comments.








All Articles