I know finalizers are not promised to be called, but is it too not promised?

326 views
Skip to first unread message

T L

unread,
Jan 28, 2017, 8:17:37 AM1/28/17
to golang-nuts


package main

import "time"
import "runtime"

type T1 struct{ i int }
type T2 struct{ i int }

func main() {
        t1 := new(T1)
        t2 := new(T2)
       
        runtime.SetFinalizer(t1, func(*T1) {println(1)})
        runtime.SetFinalizer(t2, func(*T2) {println(2)})
        runtime.GC()
        time.Sleep(time.Second * 2)
       
        // the program will output: 2
        // if I adjust the order of the declarations of t1 and t2,
        // the program will output: 1
}

Why the finalizer for the first declaration will not get called?

C Banning

unread,
Jan 28, 2017, 8:33:51 AM1/28/17
to golang-nuts
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."
Message has been deleted

T L

unread,
Jan 28, 2017, 9:25:20 AM1/28/17
to golang-nuts


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

BTW, it looks both finalizer will get executed if let the program sleep more than 10us at the beginning:

package main

import "time"
import "runtime"

func main() {
        time.Sleep(10 * time.Microsecond)
        // if sleep 10 Microsecond here, 2 and 1 will both be out
        // if sleep 1 Microsecond here, still only 2 will out
       
        t1 := new(int)
        t2 := new(int)
       
        runtime.SetFinalizer(t1, func(*int) {println(1)})
        runtime.SetFinalizer(t2, func(*int) {println(2)})
     runtime.GC()
        time.Sleep(time.Second * 20)
}
 

Dave Cheney

unread,
Jan 28, 2017, 9:33:08 AM1/28/17
to golang-nuts


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

T L

unread,
Jan 28, 2017, 9:36:44 AM1/28/17
to golang-nuts


On Saturday, January 28, 2017 at 10:25:20 PM UTC+8, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

BTW, it looks both finalizer will get executed if let the program sleep more than 10us at the beginning:

package main

import "time"
import "runtime"

func main() {
        time.Sleep(10 * time.Microsecond)
        // if sleep 10 Microsecond here, 2 and 1 will both be out
        // if sleep 1 Microsecond here, still only 2 will out
       
        t1 := new(int)
        t2 := new(int)
       
        runtime.SetFinalizer(t1, func(*int) {println(1)})
        runtime.SetFinalizer(t2, func(*int) {println(2)})
     runtime.GC()
        time.Sleep(time.Second * 20)
}
 

BTW 2, it looks that the first finalizer set in a limited escaped time for one-word data pointer will not get executed:

package main

import "time"
import "runtime"

type T1 [2]int
type T2 [2]int
// this program will output both 1 and 2,
// but if the length of T2 changes to 1, then 2 will not be output


func main() {
        t1 := new(T1)
        t2 := new(T2)
       
        runtime.SetFinalizer(t1, func(*T1) {println(1)})
        runtime.SetFinalizer(t2, func(*T2) {println(2)})
        runtime.GC()
        time.Sleep(time.Second * 1)
}
 

T L

unread,
Jan 28, 2017, 9:42:08 AM1/28/17
to golang-nuts


On Saturday, January 28, 2017 at 10:33:08 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

I know, I just want to make sure some fanalizers will get executed for a long running program.
But it looks the current implementation of SetFinalizer can't make any guarantee at all.
 

Dave Cheney

unread,
Jan 28, 2017, 9:46:50 AM1/28/17
to golang-nuts


On Sunday, 29 January 2017 01:42:08 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 10:33:08 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

I know, I just want to make sure some fanalizers will get executed for a long running program.

Finalisers are not guaranteed to run, no matter how long the program runs for, no matter how important they are.
 
But it looks the current implementation of SetFinalizer can't make any guarantee at all.

Correct. Finalisers are not guaranteed to run. You should not base the correct operation of your program on a finaliser running before your program exits.

T L

unread,
Jan 28, 2017, 10:05:13 AM1/28/17
to golang-nuts


On Saturday, January 28, 2017 at 10:46:50 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:42:08 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 10:33:08 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

I know, I just want to make sure some fanalizers will get executed for a long running program.

Finalisers are not guaranteed to run, no matter how long the program runs for, no matter how important they are.
 
But it looks the current implementation of SetFinalizer can't make any guarantee at all.

Correct. Finalisers are not guaranteed to run. You should not base the correct operation of your program on a finaliser running before your program exits.

My understanding is finalisers are guaranteed to run for some cases, but not for some other cases, for a long running program.
If any cases are not guaranteed to run, then SetFinalizer would be totally useless.

BTW, more study:

package main

import "time"
import "runtime"

type E int8
const N=2

type T [N]E

func main() {
    for i := range [8]struct{}{} {
        t := &T{0:E(i)}
        runtime.SetFinalizer(t, func(p *T) {print((*p)[0])})
    }
    runtime.GC()
    time.Sleep(time.Second * 1)
}

output:
.......E..........output(N=1)..........output(N=2)
.......int8....... ....................7654
.......int16......7654.................765432
.......int32......765432...............7654321
.......int64......7654321..............76543210

 

Konstantin Khomoutov

unread,
Jan 28, 2017, 11:02:19 AM1/28/17
to T L, golang-nuts
On Sat, 28 Jan 2017 07:05:13 -0800 (PST)
T L <tapi...@gmail.com> wrote:

[...]
> My understanding is finalisers are guaranteed to run for some cases,
> but not for some other cases, for a long running program.
> If any cases are not guaranteed to run, then SetFinalizer would be
> totally useless.
[...]

Many of your posts seem to follow a simple pattern: you posit your own
opinion about some language feature which was created because of
opinion(s) different from yours, and then you try to defend that your
opinion is the only correct one.

If finalizers were indeed totally useless, it would obviously be totally
useless to implement support for them. Please give the Go devs some
credit in that they are not that incompetent to spend their time on
useless features ;-) The line of reasoning is indeed this smiple.

Finalizers is a sort of a line of defence which _might_ help prevent
some resource leakage -- or may not, and it's fine: that's what is
called "best effort".

Please consider a type which wraps some resource provided by the
underlying OS. Let's consider an os.File. This type might have a
finalizer which would attempt to call the syscall provided by the OS to
close the file handle/descriptor wrapped in a value of that type being
finalized, but still the programmer *is required* to call Close() on
the values of that type after having finished dealing with them.
If the programmer fails to fullfill their obligations, the runtime
might help their program prevent hitting the limit of opened files, but
that'd be a best effort endeavor.

Please note that Go is not unique in this approach: in .NET (which also
features GC'ed runtime), finalizers have the same property: they are not
guaranteed to run, and you have to make sure yourself you properly deal
with objects which need to be cleaned up.

Axel Wagner

unread,
Jan 28, 2017, 12:04:35 PM1/28/17
to T L, golang-nuts
On Sat, Jan 28, 2017 at 4:05 PM, T L <tapi...@gmail.com> wrote:


On Saturday, January 28, 2017 at 10:46:50 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:42:08 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 10:33:08 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

I know, I just want to make sure some fanalizers will get executed for a long running program.

Finalisers are not guaranteed to run, no matter how long the program runs for, no matter how important they are.
 
But it looks the current implementation of SetFinalizer can't make any guarantee at all.

Correct. Finalisers are not guaranteed to run. You should not base the correct operation of your program on a finaliser running before your program exits.

My understanding is finalisers are guaranteed to run for some cases, but not for some other cases, for a long running program.

Your understanding is false. They are not guaranteed to run, fullstop.
 
If any cases are not guaranteed to run, then SetFinalizer would be totally useless.

See Konstantin's Post. They are not totally useless and their uses are documented (though I'd rephrase it as "resources that are not go-allocated memory"). You can not rely on them for correctness, but you *can* use them as an additional defense against some kind of bugs, like unclosed files. You can use them to do any kind of work that a) helps a long-running program not crash and b) is not necessary to do if your process dies. Examples are

* freeing C-allocated memory (the OS will cleanup after you, but a long-running program will crash, if it accidentally doesn't free the memory manually)
* Closing files (the OS will automatically close all files when your process exits, but a long-running program might run out of FDs and crash if it accidentally doesn't close the files)
* Munmap a memory region (the OS will unmap the files anyway when you exit, but…)

In all of these cases, your program doesn't *rely* on the Finalizer to run, for correctness, but it will be helped by it, in the presence of bugs. But a Finalizer is not a Destructor and that's also a *good* thing. Consider C++, where most of the work of Destructors is 100% unnecessary when the process exits, but is still done; now your program exiting/restarting is unnecessarily slowed down by doing work that the kernel can do more efficiently for you.
 

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

T L

unread,
Jan 28, 2017, 12:15:23 PM1/28/17
to golang-nuts, tapi...@gmail.com


On Sunday, January 29, 2017 at 1:04:35 AM UTC+8, Axel Wagner wrote:
On Sat, Jan 28, 2017 at 4:05 PM, T L <tapi...@gmail.com> wrote:


On Saturday, January 28, 2017 at 10:46:50 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:42:08 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 10:33:08 PM UTC+8, Dave Cheney wrote:


On Sunday, 29 January 2017 01:25:20 UTC+11, T L wrote:


On Saturday, January 28, 2017 at 9:33:51 PM UTC+8, C Banning wrote:
From the doc: "The finalizer for obj is scheduled to run at some arbitrary time after obj becomes unreachable. There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program."


If this is true, then the SetFinalizer function would be much meaningless.

Yes. The only reasonable way to interpret the operation of SetFinalizer is to assume it does not thing and program accordingly.

To repeat, you cannot base the correct operation of your program on a finaliser running before your program exits. 

I know, I just want to make sure some fanalizers will get executed for a long running program.

Finalisers are not guaranteed to run, no matter how long the program runs for, no matter how important they are.
 
But it looks the current implementation of SetFinalizer can't make any guarantee at all.

Correct. Finalisers are not guaranteed to run. You should not base the correct operation of your program on a finaliser running before your program exits.

My understanding is finalisers are guaranteed to run for some cases, but not for some other cases, for a long running program.

Your understanding is false. They are not guaranteed to run, fullstop.
 
If any cases are not guaranteed to run, then SetFinalizer would be totally useless.

See Konstantin's Post. They are not totally useless and their uses are documented (though I'd rephrase it as "resources that are not go-allocated memory"). You can not rely on them for correctness, but you *can* use them as an additional defense against some kind of bugs, like unclosed files. You can use them to do any kind of work that a) helps a long-running program not crash and b) is not necessary to do if your process dies. Examples are

* freeing C-allocated memory (the OS will cleanup after you, but a long-running program will crash, if it accidentally doesn't free the memory manually)
* Closing files (the OS will automatically close all files when your process exits, but a long-running program might run out of FDs and crash if it accidentally doesn't close the files)
* Munmap a memory region (the OS will unmap the files anyway when you exit, but…)

In all of these cases, your program doesn't *rely* on the Finalizer to run, for correctness, but it will be helped by it, in the presence of bugs. But a Finalizer is not a Destructor and that's also a *good* thing. Consider C++, where most of the work of Destructors is 100% unnecessary when the process exits, but is still done; now your program exiting/restarting is unnecessarily slowed down by doing work that the kernel can do more efficiently for you.

Ok, good explanation (as well as the explanation of Konstantin Khomoutov).

So SetFinalizer is for lib maintainers to avoid lib users not doing things correctly, right?
This is not well emphasized in the API docs.

 
 
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Dave Cheney

unread,
Jan 28, 2017, 2:39:31 PM1/28/17
to golang-nuts
If you find a piece of code that uses a finaliser for the correct operation of that program, that code is broken.

John Souvestre

unread,
Jan 28, 2017, 4:22:20 PM1/28/17
to golang-nuts
> If finalizers were indeed totally useless, it would obviously be totally
useless to implement support for them.

If someone described a few cases where finalizers were useful perhaps it would help understand them.

John

John Souvestre - New Orleans LA


John Souvestre

unread,
Jan 28, 2017, 4:35:30 PM1/28/17
to golang-nuts
> If you find a piece of code that uses a finaliser for the correct operation of that program, that code is broken.

Does that include using a finalizer with CGO code? From what I read, that seems to be where they are most often used.

> https://groups.google.com/forum/#!topic/golang-dev/DMiUkpS1uyQ

Justin Israel

unread,
Jan 28, 2017, 4:49:54 PM1/28/17
to John Souvestre, golang-nuts


On Sun, Jan 29, 2017, 10:35 AM John Souvestre <jo...@souvestre.com> wrote:
 > If you find a piece of code that uses a finaliser for the correct operation of that program, that code is broken.

Does that include using a finalizer with CGO code?  From what I read, that seems to be where they are most often used.

 > https://groups.google.com/forum/#!topic/golang-dev/DMiUkpS1uyQ

But in the case of using them to free cgo resources, one wouldn't (hopefully) be doing that for correct operation of the program. Just to ensure memory gets freed. If one were doing it to rely on C++ destructors triggering some other important behavior to the program, when a finalizer triggers the destructor,  then it would be wrong. 



John

    John Souvestre - New Orleans LA

Axel Wagner

unread,
Jan 28, 2017, 4:50:09 PM1/28/17
to John Souvestre, golang-nuts
There are a bunch of examples of where they are *useful* (some in this thread). It's just, that you can not *rely* on them for correctness. If you are setting a finalizer, because something needs to happen before some value gets GCed/falls out of scope, you are doing it wrong, don't do it. If you are adhering to that and you *still* want to set a finalizer, it's probably useful. Go ahead.

On Sat, Jan 28, 2017 at 10:35 PM, John Souvestre <jo...@souvestre.com> wrote:
 > If you find a piece of code that uses a finaliser for the correct operation of that program, that code is broken.

Does that include using a finalizer with CGO code?  From what I read, that seems to be where they are most often used.

If it relies on them for correctness; yes. Most cases probably don't, they use it to tie the lifetime of some C-allocated memory to the lifetime of some go value encapsulating it.

(You should still keep in mind, that someone else might very well also set a finalizer on some value you are giving to them, overriding yours. This is AFAIK not trivial to resolve, though.)
 

 > https://groups.google.com/forum/#!topic/golang-dev/DMiUkpS1uyQ

John

    John Souvestre - New Orleans LA

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Jesper Louis Andersen

unread,
Jan 29, 2017, 10:28:09 AM1/29/17
to John Souvestre, golang-nuts
On Sat, Jan 28, 2017 at 10:22 PM John Souvestre <jo...@souvestre.com> wrote:
 > If finalizers were indeed totally useless, it would obviously be totally
useless to implement support for them.

If someone described a few cases where finalizers were useful perhaps it would help understand them.


The common case usually has to do with some kind of finite resource of which you have "enough", but as the program runs, you'd like to eventually make sure that you don't leak said resource and give it back to the kernel so it can be reused.

Another common case is when you have some kind of registry to which you associate properties of objects in the system. If the object somehow gets collected, you'd like the entry to be removed from the registry eventually. Keeping it there for a while isn't a problem, but keeping it for the rest of the programs lifetime is. it is much like garbage in a GC: as long as you eventually get to the garbage and sweep it to the free-list, you are golden.

Finalizers are a special case of a more general "ephemeron" construction, which can avoid some of the common trouble finalizers tend to have. There are some subtle interactions in which a (strong) reference keeps an object alive in the GC and thus creates a leak anyway.

The reason finalizers (ephemerons) are useful is that library authors can present somewhat simpler APIs in some cases. Rather than you having to do some cleaning, the library makes sure that it handles it itself, though with added resource pressure as a result.

Val

unread,
Jan 29, 2017, 4:51:01 PM1/29/17
to golang-nuts, jo...@souvestre.com
Hello Jesper
if I understand this thread correctly, "make sure" should be rephrased by "make extra efforts for ...".

Sorry for nitpicking  (not trying to look pedantic or arrogant)
Val


On Sunday, January 29, 2017 at 4:28:09 PM UTC+1, Jesper Louis Andersen wrote:
 you'd like to eventually make sure that you don't leak said resource
...

Michael Jones

unread,
Jan 29, 2017, 9:38:59 PM1/29/17
to Val, golang-nuts, jo...@souvestre.com
"endeavor to"
"anticipate"
"allow for"
"prepare for"
"encourage"
"facilitate"
   ...good

"ensure" / "force" / "guarantee" (not in your control)
"reclaim" / "recycle" / "release" (too absolute)
   ...bad

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Michael T. Jones
michae...@gmail.com

John Souvestre

unread,
Jan 29, 2017, 10:15:31 PM1/29/17
to golang-nuts

But is it possible to finalize a discussion about finalizers?

 

John

    John Souvestre - New Orleans LA

 

To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.


For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages