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

cloudpickle_fast causes pickled object to be uncollectable #343

Closed
edoakes opened this issue Feb 13, 2020 · 4 comments · Fixed by ray-project/ray#7177
Closed

cloudpickle_fast causes pickled object to be uncollectable #343

edoakes opened this issue Feb 13, 2020 · 4 comments · Fixed by ray-project/ray#7177

Comments

@edoakes
Copy link

edoakes commented Feb 13, 2020

I work on Ray, where we're now using cloudpickle_fast for object serialization (thanks for the great work!). We have recently started working on distributed reference counting in Ray, which depends on local Python object reference counting.

When running some tests for our reference counting implementation, we ran into an issue where some serialized objects were uncollectable (due to a circular reference), causing them not to be deleted until a call to the garbage collector. After some investigation, it seems the problem is in cloudpickle_fast as shown by the following example:

import cloudpickle
import gc

class ShortLivedObject:
    def __del__(self):
        print("Went out of scope!")

cloudpickle.dumps(ShortLivedObject())

print("GC'ing...")
gc.collect()

We expect that the __del__ method of the ShortLivedObject will be called after pickle.dumps returns, as the only references to the object are removed. When running this example on Python 3.7 or earlier (not using cloudpickle_fast), this is indeed the case. However, when running on Python 3.8 (using cloudpickle_fast), ShortLivedObject.__del__ is not called until the call to gc.collect().

Any help would be much appreciated!

FYI: setting gc.DEBUG_SAVEALL and printing gc.garbage after the collect call shows the following:

<cell at 0x7f9030ceddc8: tuple object at 0x7f9030d30470>
<cell at 0x7f9030ceddf8: type object at 0x7f90674201d8>
(<class 'type'>,)
(<cell at 0x7f9030ceddc8: tuple object at 0x7f9030d30470>, <cell at 0x7f9030ceddf8: type object at 0x7f90674201d8>)
<function with_metaclass.<locals>.metaclass.__new__ at 0x7f9030d44158>
(<cell at 0x7f9030ceddc8: tuple object at 0x7f9030d30470>, <cell at 0x7f9030ceddf8: type object at 0x7f90674201d8>)
<function with_metaclass.<locals>.metaclass.__prepare__ at 0x7f9030d441e0>
<classmethod object at 0x7f9030d30d30>
{'__module__': 'six', '__new__': <staticmethod object at 0x7f9030d30d68>, '__prepare__': <classmethod object at 0x7f9030d30d30>, '__doc__': None}
<class 'six.with_metaclass.<locals>.metaclass'>
<staticmethod object at 0x7f9030d30d68>
(<class 'six.with_metaclass.<locals>.metaclass'>, <class 'type'>, <class 'object'>)
(<class 'object'>,)
<class 'six.temporary_class'>
<attribute '__dict__' of 'temporary_class' objects>
{'__module__': 'six', '__dict__': <attribute '__dict__' of 'temporary_class' objects>, '__weakref__': <attribute '__weakref__' of 'temporary_class' objects>, '__doc__': None}
<attribute '__weakref__' of 'temporary_class' objects>
(<class 'six.temporary_class'>, <class 'object'>)
<_io.BytesIO object at 0x7f9030d36200>
<ray.cloudpickle.cloudpickle_fast.CloudPickler object at 0x7f9030c85948>
<built-in method write of _io.BytesIO object at 0x7f9030d36200>
{'globals_ref': {140258197779872: {'__package__': None, '__name__': '__main__', '__file__': 'ref2.py'}}, 'proto': 4}
{<class 'complex'>: <function pickle_complex at 0x7f905817fb70>, <class '_sre.SRE_Pattern'>: <function _pickle at 0x7f905817f9d8>, <class 'numpy.ufunc'>: <function _ufunc_reduce at 0x7f9048311950>, <class 'classmethod'>: <function _classmethod_reduce at 0x7f9030399f28>, <class '_io.TextIOWrapper'>: <function _file_reduce at 0x7f903039a048>, <class 'logging.Logger'>: <function _logger_reduce at 0x7f903039a378>, <class 'logging.RootLogger'>: <function _root_logger_reduce at 0x7f903039a400>, <class 'memoryview'>: <function _memoryview_reduce at 0x7f903039a1e0>, <class 'staticmethod'>: <function _classmethod_reduce at 0x7f9030399f28>, <class 'code'>: <function _code_reduce at 0x7f9030399d90>, <class 'getset_descriptor'>: <function _getset_descriptor_reduce at 0x7f903039a0d0>, <class 'module'>: <function _module_reduce at 0x7f903039a268>, <class 'method'>: <function _method_reduce at 0x7f903039a2f0>, <class 'mappingproxy'>: <function _mappingproxy_reduce at 0x7f903039a158>, <class '_weakrefset.WeakSet'>: <function _weakset_reduce at 0x7f903039a488>, <class 'cell'>: <function _cell_reduce at 0x7f9030399ea0>, <class 'property'>: <function _property_reduce at 0x7f903039a730>}
<bound method CloudPickler.reducer_override of <ray.cloudpickle.cloudpickle_fast.CloudPickler object at 0x7f9030c85948>>
{140258197779872: {'__package__': None, '__name__': '__main__', '__file__': 'ref2.py'}}
@ogrisel
Copy link
Contributor

ogrisel commented Feb 14, 2020

@pierreglaser could this be a duplicate of #327 fixed upstream in https://bugs.python.org/issue39492?

@pierreglaser
Copy link
Member

Yes. @edoakes this bug will be fixed starting Python 3.8.2.

@ogrisel
Copy link
Contributor

ogrisel commented Feb 14, 2020

I enabled test on Python nightly by default in #344 and I confirm that the new test introduced in:

https://github.com/cloudpipe/cloudpickle/pull/334/files

is not skipped in Python 3.9-dev:

https://github.com/cloudpipe/cloudpickle/pull/344/checks?check_run_id=446363404

while it is skipped on Python 3.8.1:

https://github.com/cloudpipe/cloudpickle/pull/344/checks?check_run_id=446363575

@pierreglaser
Copy link
Member

Closing this since it got resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants