Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: sync: add function to free objects removed from a Pool #23216

Closed
neild opened this issue Dec 21, 2017 · 15 comments
Closed

proposal: sync: add function to free objects removed from a Pool #23216

neild opened this issue Dec 21, 2017 · 15 comments

Comments

@neild
Copy link
Contributor

neild commented Dec 21, 2017

Items stored in a sync.Pool may be silently removed at any time. This makes a Pool unsuited for managing objects which require explicit deallocation; e.g., objects with an associated open file or active goroutine.

Proposal: Add a Release field to sync.Pool containing a function which is called with items automatically removed from the pool. Add a Close method which removes all objects from the pool.

type Pool struct {
	// New optionally specifies a function to generate
	// a value when Get would otherwise return nil.
	// It may not be changed concurrently with calls to Get.
	New func() interface{}

	// Release optionally specifies a function called when
	// an item is removed from the pool. Release is not called
	// for items returned by Put.
	Release func(interface{})
}

// Close removes all items from the pool.
func (*Pool) Close()
@cznic
Copy link
Contributor

cznic commented Dec 21, 2017

This makes a Pool unsuited for managing objects which require explicit deallocation; e.g., objects with an associated open file or active goroutine.

Why put such objects in the pool in the first place?

@davecheney
Copy link
Contributor

sync.Pool is not a cache, that was an explicit design decision.

@neild
Copy link
Contributor Author

neild commented Dec 21, 2017

Why put such objects in the pool in the first place?

Why put any object in a pool?

sync.Pool is not a cache, that was an explicit design decision.

Someone should update the documentation, then: "Pool's purpose is to cache allocated but unused items for later reuse..."

@dsnet
Copy link
Member

dsnet commented Dec 21, 2017

In CL/41860043, Russ states the following:

I answered your question about eviction signals by pointing out that this
is not a cache. It is a free list. "Eviction" is for caches. A cache holds
a set of non-interchangeable objects, each of which represents a different
computation or effort that might be repeated if an eviction is necessary.
In a cache, it is important that the eviction policy predict the future
needs as well as possible so that the impact of an eviction is put off for
as long as possible. A free list holds a set of interchangeable,
indistinguishable pieces of memory, each of which is just sitting waiting
to be used for bypassing a memory allocation and holds no other value.
"Eviction" is not something that makes sense for free lists.

@davecheney
Copy link
Contributor

@neild yup, that is really bad wording. I remember at the time the discussion went something along the lines of “it’s called sync.Pool, because it doesn’t have cache semantics, it can happily throw away anything you put in it”

@odeke-em odeke-em changed the title sync: add function to free objects removed from a sync.Pool proposal: sync: add function to free objects removed from a Pool Dec 21, 2017
@gopherbot gopherbot added this to the Proposal milestone Dec 21, 2017
@neild
Copy link
Contributor Author

neild commented Dec 21, 2017

I don't want to quibble on the semantics of "cache" vs. "free list", but the question of whether objects require release is orthogonal to whether they are interchangeable.

I believe Russ's comment in CL/41860043 is regarding eviction policy in the sense of deciding when and whether to remove objects from the pool. This feature request isn't about controlling when items are removed from the pool, but to have a way to clean them up when they are.

@dsnet
Copy link
Member

dsnet commented Dec 21, 2017

Practically speaking, the implementation of sync.Pool performs cleanup at a stop-the-world moment with the additional restriction that the cleanup logic cannot allocate. I'm no expert in GC, but it seems that any implementation of Pool.Release would have the same restrictions where 1) the user's function should not allocate, and 2) the function is fast to avoid hurting the GC pause time. I would not want any API that ever exposes these details to the user.

@AlexRouSg
Copy link
Contributor

If sync.Pool cleans up at GC then why not just use runtime.Setfinalizer?
https://golang.org/pkg/runtime/#SetFinalizer

@CAFxX
Copy link
Contributor

CAFxX commented Dec 21, 2017

@neild I fully agree with @AlexRouSg, that's exactly what finalizers are for, so much so that they are used in the standard library exactly for the use cases you're describing (closing file descriptors associated with an object being GCed):

runtime.SetFinalizer(fd, (*netFD).Close)

runtime.SetFinalizer(f.file, (*file).close)

@neild
Copy link
Contributor Author

neild commented Dec 21, 2017

Finalizers don't work for an object with an associated goroutine.

@CAFxX
Copy link
Contributor

CAFxX commented Dec 21, 2017 via email

@chowey
Copy link

chowey commented Dec 25, 2017

If a goroutine holds a pointer to an object that is also held by a sync.Pool, you are probably using sync.Pool incorrectly.

@bcmills
Copy link
Contributor

bcmills commented Jan 3, 2018

@chowey You've got it backwards. I suspect @neild is talking about a sync.Pool that holds an object that points to something that the goroutine also points to (e.g., a set of channels for communicating with that goroutine).

@rsc
Copy link
Contributor

rsc commented Jan 8, 2018

I'm sure you can make finalizers work for this, in the worst case by putting in the pool a wrapper that has the finalizer attached but is not referenced by any other goroutine. Please do that.

I regret having one kind of finalizer already. I would regret having two kinds at least four times as much. (You can decide whether my regret is quadratic or exponential in the number of finalizer kinds.)

/cc @aclements @RLH

@rsc rsc closed this as completed Jan 8, 2018
@andlabs
Copy link
Contributor

andlabs commented Jan 16, 2018

Also:

Someone should update the documentation, then: "Pool's purpose is to cache allocated but unused items for later reuse..."

I can see why this wording would seem appropriate — in this case, what's being cached is unused items, not used items. But I suppose, given this thread, a question would be does this merit rewording? I'm not sure how much more obscure the term "free list" is.

@golang golang locked and limited conversation to collaborators Jan 16, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests