You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1400 lines
48 KiB

bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........
17 years ago
Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........
17 years ago
Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........
17 years ago
Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........
17 years ago
Merged revisions 70912,70944,70968,71033,71041,71208,71263,71286,71395-71396,71405-71406,71485,71492,71494 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r70912 | georg.brandl | 2009-03-31 17:35:46 -0500 (Tue, 31 Mar 2009) | 1 line #5617: add a handy function to print a unicode string to gdbinit. ........ r70944 | georg.brandl | 2009-03-31 23:32:39 -0500 (Tue, 31 Mar 2009) | 1 line #5631: add upload to list of possible commands, which is presented in --help-commands. ........ r70968 | michael.foord | 2009-04-01 13:25:38 -0500 (Wed, 01 Apr 2009) | 1 line Adding Wing project file ........ r71033 | brett.cannon | 2009-04-01 22:34:53 -0500 (Wed, 01 Apr 2009) | 3 lines Fix two issues introduced by issue #71031 by changing the signature of PyImport_AppendInittab() to take a const char *. ........ r71041 | jesse.noller | 2009-04-02 00:17:26 -0500 (Thu, 02 Apr 2009) | 1 line Add custom initializer argument to multiprocess.Manager*, courtesy of lekma ........ r71208 | michael.foord | 2009-04-04 20:15:01 -0500 (Sat, 04 Apr 2009) | 4 lines Change the way unittest.TestSuite use their tests to always access them through iteration. Non behavior changing, this allows you to create custom subclasses that override __iter__. Issue #5693 ........ r71263 | michael.foord | 2009-04-05 14:19:28 -0500 (Sun, 05 Apr 2009) | 4 lines Adding assertIs and assertIsNot methods to unittest.TestCase Issue #2578 ........ r71286 | tarek.ziade | 2009-04-05 17:04:38 -0500 (Sun, 05 Apr 2009) | 1 line added a simplest test to distutils.spawn._nt_quote_args ........ r71395 | benjamin.peterson | 2009-04-08 08:27:29 -0500 (Wed, 08 Apr 2009) | 1 line these must be installed to correctly run tests ........ r71396 | benjamin.peterson | 2009-04-08 08:29:41 -0500 (Wed, 08 Apr 2009) | 1 line fix syntax ........ r71405 | andrew.kuchling | 2009-04-09 06:22:47 -0500 (Thu, 09 Apr 2009) | 1 line Add items ........ r71406 | andrew.kuchling | 2009-04-09 06:23:36 -0500 (Thu, 09 Apr 2009) | 1 line Typo fixes ........ r71485 | andrew.kuchling | 2009-04-11 11:12:23 -0500 (Sat, 11 Apr 2009) | 1 line Add various items ........ r71492 | georg.brandl | 2009-04-11 13:19:27 -0500 (Sat, 11 Apr 2009) | 1 line Take credit for a patch of mine. ........ r71494 | benjamin.peterson | 2009-04-11 14:31:00 -0500 (Sat, 11 Apr 2009) | 1 line ignore py3_test_grammar when compiling the library ........
17 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
bpo-35813: Tests and docs for shared_memory (#11816) * Added tests for shared_memory submodule. * Added tests for ShareableList. * Fix bug in allocationn size during creation of empty ShareableList illuminated by existing test run on Linux. * Initial set of docs for shared_memory module. * Added docs for ShareableList, added doctree entry for shared_memory submodule, name refactoring for greater clarity. * Added examples to SharedMemoryManager docs, for ease of documentation switched away from exclusively registered functions to some explicit methods on SharedMemoryManager. * Wording tweaks to docs. * Fix test failures on Windows. * Added tests around SharedMemoryManager. * Documentation tweaks. * Fix inappropriate test on Windows. * Further documentation tweaks. * Fix bare exception. * Removed __copyright__. * Fixed typo in doc, removed comment. * Updated SharedMemoryManager preliminary tests to reflect change of not supporting all registered functions on SyncManager. * Added Sphinx doctest run controls. * CloseHandle should be in a finally block in case MapViewOfFile fails. * Missed opportunity to use with statement. * Switch to self.addCleanup to spare long try/finally blocks and save one indentation, change to use decorator to skip test instead. * Simplify the posixshmem extension module. Provide shm_open() and shm_unlink() functions. Move other functionality into the shared_memory.py module. * Added to doc around size parameter of SharedMemory. * Changed PosixSharedMemory.size to use os.fstat. * Change SharedMemory.buf to a read-only property as well as NamedSharedMemory.size. * Marked as provisional per PEP411 in docstring. * Changed SharedMemoryTracker to be private. * Removed registered Proxy Objects from SharedMemoryManager. * Removed shareable_wrap(). * Removed shareable_wrap() and dangling references to it. * For consistency added __reduce__ to key classes. * Fix for potential race condition on Windows for O_CREX. * Remove unused imports. * Update access to kernel32 on Windows per feedback from eryksun. * Moved kernel32 calls to _winapi. * Removed ShareableList.copy as redundant. * Changes to _winapi use from eryksun feedback. * Adopt simpler SharedMemory API, collapsing PosixSharedMemory and WindowsNamedSharedMemory into one. * Fix missing docstring on class, add test for ignoring size when attaching. * Moved SharedMemoryManager to managers module, tweak to fragile test. * Tweak to exception in OpenFileMapping suggested by eryksun. * Mark a few dangling bits as private as suggested by Giampaolo.
7 years ago
  1. #
  2. # Module providing manager classes for dealing
  3. # with shared objects
  4. #
  5. # multiprocessing/managers.py
  6. #
  7. # Copyright (c) 2006-2008, R Oudkerk
  8. # Licensed to PSF under a Contributor Agreement.
  9. #
  10. __all__ = [ 'BaseManager', 'SyncManager', 'BaseProxy', 'Token',
  11. 'SharedMemoryManager' ]
  12. #
  13. # Imports
  14. #
  15. import sys
  16. import threading
  17. import signal
  18. import array
  19. import queue
  20. import time
  21. import os
  22. from os import getpid
  23. from traceback import format_exc
  24. from . import connection
  25. from .context import reduction, get_spawning_popen, ProcessError
  26. from . import pool
  27. from . import process
  28. from . import util
  29. from . import get_context
  30. try:
  31. from . import shared_memory
  32. HAS_SHMEM = True
  33. except ImportError:
  34. HAS_SHMEM = False
  35. #
  36. # Register some things for pickling
  37. #
  38. def reduce_array(a):
  39. return array.array, (a.typecode, a.tobytes())
  40. reduction.register(array.array, reduce_array)
  41. view_types = [type(getattr({}, name)()) for name in ('items','keys','values')]
  42. if view_types[0] is not list: # only needed in Py3.0
  43. def rebuild_as_list(obj):
  44. return list, (list(obj),)
  45. for view_type in view_types:
  46. reduction.register(view_type, rebuild_as_list)
  47. #
  48. # Type for identifying shared objects
  49. #
  50. class Token(object):
  51. '''
  52. Type to uniquely indentify a shared object
  53. '''
  54. __slots__ = ('typeid', 'address', 'id')
  55. def __init__(self, typeid, address, id):
  56. (self.typeid, self.address, self.id) = (typeid, address, id)
  57. def __getstate__(self):
  58. return (self.typeid, self.address, self.id)
  59. def __setstate__(self, state):
  60. (self.typeid, self.address, self.id) = state
  61. def __repr__(self):
  62. return '%s(typeid=%r, address=%r, id=%r)' % \
  63. (self.__class__.__name__, self.typeid, self.address, self.id)
  64. #
  65. # Function for communication with a manager's server process
  66. #
  67. def dispatch(c, id, methodname, args=(), kwds={}):
  68. '''
  69. Send a message to manager using connection `c` and return response
  70. '''
  71. c.send((id, methodname, args, kwds))
  72. kind, result = c.recv()
  73. if kind == '#RETURN':
  74. return result
  75. raise convert_to_error(kind, result)
  76. def convert_to_error(kind, result):
  77. if kind == '#ERROR':
  78. return result
  79. elif kind in ('#TRACEBACK', '#UNSERIALIZABLE'):
  80. if not isinstance(result, str):
  81. raise TypeError(
  82. "Result {0!r} (kind '{1}') type is {2}, not str".format(
  83. result, kind, type(result)))
  84. if kind == '#UNSERIALIZABLE':
  85. return RemoteError('Unserializable message: %s\n' % result)
  86. else:
  87. return RemoteError(result)
  88. else:
  89. return ValueError('Unrecognized message type {!r}'.format(kind))
  90. class RemoteError(Exception):
  91. def __str__(self):
  92. return ('\n' + '-'*75 + '\n' + str(self.args[0]) + '-'*75)
  93. #
  94. # Functions for finding the method names of an object
  95. #
  96. def all_methods(obj):
  97. '''
  98. Return a list of names of methods of `obj`
  99. '''
  100. temp = []
  101. for name in dir(obj):
  102. func = getattr(obj, name)
  103. if callable(func):
  104. temp.append(name)
  105. return temp
  106. def public_methods(obj):
  107. '''
  108. Return a list of names of methods of `obj` which do not start with '_'
  109. '''
  110. return [name for name in all_methods(obj) if name[0] != '_']
  111. #
  112. # Server which is run in a process controlled by a manager
  113. #
  114. class Server(object):
  115. '''
  116. Server class which runs in a process controlled by a manager object
  117. '''
  118. public = ['shutdown', 'create', 'accept_connection', 'get_methods',
  119. 'debug_info', 'number_of_objects', 'dummy', 'incref', 'decref']
  120. def __init__(self, registry, address, authkey, serializer):
  121. if not isinstance(authkey, bytes):
  122. raise TypeError(
  123. "Authkey {0!r} is type {1!s}, not bytes".format(
  124. authkey, type(authkey)))
  125. self.registry = registry
  126. self.authkey = process.AuthenticationString(authkey)
  127. Listener, Client = listener_client[serializer]
  128. # do authentication later
  129. self.listener = Listener(address=address, backlog=16)
  130. self.address = self.listener.address
  131. self.id_to_obj = {'0': (None, ())}
  132. self.id_to_refcount = {}
  133. self.id_to_local_proxy_obj = {}
  134. self.mutex = threading.Lock()
  135. def serve_forever(self):
  136. '''
  137. Run the server forever
  138. '''
  139. self.stop_event = threading.Event()
  140. process.current_process()._manager_server = self
  141. try:
  142. accepter = threading.Thread(target=self.accepter)
  143. accepter.daemon = True
  144. accepter.start()
  145. try:
  146. while not self.stop_event.is_set():
  147. self.stop_event.wait(1)
  148. except (KeyboardInterrupt, SystemExit):
  149. pass
  150. finally:
  151. if sys.stdout != sys.__stdout__: # what about stderr?
  152. util.debug('resetting stdout, stderr')
  153. sys.stdout = sys.__stdout__
  154. sys.stderr = sys.__stderr__
  155. sys.exit(0)
  156. def accepter(self):
  157. while True:
  158. try:
  159. c = self.listener.accept()
  160. except OSError:
  161. continue
  162. t = threading.Thread(target=self.handle_request, args=(c,))
  163. t.daemon = True
  164. t.start()
  165. def handle_request(self, c):
  166. '''
  167. Handle a new connection
  168. '''
  169. funcname = result = request = None
  170. try:
  171. connection.deliver_challenge(c, self.authkey)
  172. connection.answer_challenge(c, self.authkey)
  173. request = c.recv()
  174. ignore, funcname, args, kwds = request
  175. assert funcname in self.public, '%r unrecognized' % funcname
  176. func = getattr(self, funcname)
  177. except Exception:
  178. msg = ('#TRACEBACK', format_exc())
  179. else:
  180. try:
  181. result = func(c, *args, **kwds)
  182. except Exception:
  183. msg = ('#TRACEBACK', format_exc())
  184. else:
  185. msg = ('#RETURN', result)
  186. try:
  187. c.send(msg)
  188. except Exception as e:
  189. try:
  190. c.send(('#TRACEBACK', format_exc()))
  191. except Exception:
  192. pass
  193. util.info('Failure to send message: %r', msg)
  194. util.info(' ... request was %r', request)
  195. util.info(' ... exception was %r', e)
  196. c.close()
  197. def serve_client(self, conn):
  198. '''
  199. Handle requests from the proxies in a particular process/thread
  200. '''
  201. util.debug('starting server thread to service %r',
  202. threading.current_thread().name)
  203. recv = conn.recv
  204. send = conn.send
  205. id_to_obj = self.id_to_obj
  206. while not self.stop_event.is_set():
  207. try:
  208. methodname = obj = None
  209. request = recv()
  210. ident, methodname, args, kwds = request
  211. try:
  212. obj, exposed, gettypeid = id_to_obj[ident]
  213. except KeyError as ke:
  214. try:
  215. obj, exposed, gettypeid = \
  216. self.id_to_local_proxy_obj[ident]
  217. except KeyError as second_ke:
  218. raise ke
  219. if methodname not in exposed:
  220. raise AttributeError(
  221. 'method %r of %r object is not in exposed=%r' %
  222. (methodname, type(obj), exposed)
  223. )
  224. function = getattr(obj, methodname)
  225. try:
  226. res = function(*args, **kwds)
  227. except Exception as e:
  228. msg = ('#ERROR', e)
  229. else:
  230. typeid = gettypeid and gettypeid.get(methodname, None)
  231. if typeid:
  232. rident, rexposed = self.create(conn, typeid, res)
  233. token = Token(typeid, self.address, rident)
  234. msg = ('#PROXY', (rexposed, token))
  235. else:
  236. msg = ('#RETURN', res)
  237. except AttributeError:
  238. if methodname is None:
  239. msg = ('#TRACEBACK', format_exc())
  240. else:
  241. try:
  242. fallback_func = self.fallback_mapping[methodname]
  243. result = fallback_func(
  244. self, conn, ident, obj, *args, **kwds
  245. )
  246. msg = ('#RETURN', result)
  247. except Exception:
  248. msg = ('#TRACEBACK', format_exc())
  249. except EOFError:
  250. util.debug('got EOF -- exiting thread serving %r',
  251. threading.current_thread().name)
  252. sys.exit(0)
  253. except Exception:
  254. msg = ('#TRACEBACK', format_exc())
  255. try:
  256. try:
  257. send(msg)
  258. except Exception as e:
  259. send(('#UNSERIALIZABLE', format_exc()))
  260. except Exception as e:
  261. util.info('exception in thread serving %r',
  262. threading.current_thread().name)
  263. util.info(' ... message was %r', msg)
  264. util.info(' ... exception was %r', e)
  265. conn.close()
  266. sys.exit(1)
  267. def fallback_getvalue(self, conn, ident, obj):
  268. return obj
  269. def fallback_str(self, conn, ident, obj):
  270. return str(obj)
  271. def fallback_repr(self, conn, ident, obj):
  272. return repr(obj)
  273. fallback_mapping = {
  274. '__str__':fallback_str,
  275. '__repr__':fallback_repr,
  276. '#GETVALUE':fallback_getvalue
  277. }
  278. def dummy(self, c):
  279. pass
  280. def debug_info(self, c):
  281. '''
  282. Return some info --- useful to spot problems with refcounting
  283. '''
  284. # Perhaps include debug info about 'c'?
  285. with self.mutex:
  286. result = []
  287. keys = list(self.id_to_refcount.keys())
  288. keys.sort()
  289. for ident in keys:
  290. if ident != '0':
  291. result.append(' %s: refcount=%s\n %s' %
  292. (ident, self.id_to_refcount[ident],
  293. str(self.id_to_obj[ident][0])[:75]))
  294. return '\n'.join(result)
  295. def number_of_objects(self, c):
  296. '''
  297. Number of shared objects
  298. '''
  299. # Doesn't use (len(self.id_to_obj) - 1) as we shouldn't count ident='0'
  300. return len(self.id_to_refcount)
  301. def shutdown(self, c):
  302. '''
  303. Shutdown this process
  304. '''
  305. try:
  306. util.debug('manager received shutdown message')
  307. c.send(('#RETURN', None))
  308. except:
  309. import traceback
  310. traceback.print_exc()
  311. finally:
  312. self.stop_event.set()
  313. def create(*args, **kwds):
  314. '''
  315. Create a new shared object and return its id
  316. '''
  317. if len(args) >= 3:
  318. self, c, typeid, *args = args
  319. elif not args:
  320. raise TypeError("descriptor 'create' of 'Server' object "
  321. "needs an argument")
  322. else:
  323. if 'typeid' not in kwds:
  324. raise TypeError('create expected at least 2 positional '
  325. 'arguments, got %d' % (len(args)-1))
  326. typeid = kwds.pop('typeid')
  327. if len(args) >= 2:
  328. self, c, *args = args
  329. import warnings
  330. warnings.warn("Passing 'typeid' as keyword argument is deprecated",
  331. DeprecationWarning, stacklevel=2)
  332. else:
  333. if 'c' not in kwds:
  334. raise TypeError('create expected at least 2 positional '
  335. 'arguments, got %d' % (len(args)-1))
  336. c = kwds.pop('c')
  337. self, *args = args
  338. import warnings
  339. warnings.warn("Passing 'c' as keyword argument is deprecated",
  340. DeprecationWarning, stacklevel=2)
  341. args = tuple(args)
  342. with self.mutex:
  343. callable, exposed, method_to_typeid, proxytype = \
  344. self.registry[typeid]
  345. if callable is None:
  346. if kwds or (len(args) != 1):
  347. raise ValueError(
  348. "Without callable, must have one non-keyword argument")
  349. obj = args[0]
  350. else:
  351. obj = callable(*args, **kwds)
  352. if exposed is None:
  353. exposed = public_methods(obj)
  354. if method_to_typeid is not None:
  355. if not isinstance(method_to_typeid, dict):
  356. raise TypeError(
  357. "Method_to_typeid {0!r}: type {1!s}, not dict".format(
  358. method_to_typeid, type(method_to_typeid)))
  359. exposed = list(exposed) + list(method_to_typeid)
  360. ident = '%x' % id(obj) # convert to string because xmlrpclib
  361. # only has 32 bit signed integers
  362. util.debug('%r callable returned object with id %r', typeid, ident)
  363. self.id_to_obj[ident] = (obj, set(exposed), method_to_typeid)
  364. if ident not in self.id_to_refcount:
  365. self.id_to_refcount[ident] = 0
  366. self.incref(c, ident)
  367. return ident, tuple(exposed)
  368. create.__text_signature__ = '($self, c, typeid, /, *args, **kwds)'
  369. def get_methods(self, c, token):
  370. '''
  371. Return the methods of the shared object indicated by token
  372. '''
  373. return tuple(self.id_to_obj[token.id][1])
  374. def accept_connection(self, c, name):
  375. '''
  376. Spawn a new thread to serve this connection
  377. '''
  378. threading.current_thread().name = name
  379. c.send(('#RETURN', None))
  380. self.serve_client(c)
  381. def incref(self, c, ident):
  382. with self.mutex:
  383. try:
  384. self.id_to_refcount[ident] += 1
  385. except KeyError as ke:
  386. # If no external references exist but an internal (to the
  387. # manager) still does and a new external reference is created
  388. # from it, restore the manager's tracking of it from the
  389. # previously stashed internal ref.
  390. if ident in self.id_to_local_proxy_obj:
  391. self.id_to_refcount[ident] = 1
  392. self.id_to_obj[ident] = \
  393. self.id_to_local_proxy_obj[ident]
  394. obj, exposed, gettypeid = self.id_to_obj[ident]
  395. util.debug('Server re-enabled tracking & INCREF %r', ident)
  396. else:
  397. raise ke
  398. def decref(self, c, ident):
  399. if ident not in self.id_to_refcount and \
  400. ident in self.id_to_local_proxy_obj:
  401. util.debug('Server DECREF skipping %r', ident)
  402. return
  403. with self.mutex:
  404. if self.id_to_refcount[ident] <= 0:
  405. raise AssertionError(
  406. "Id {0!s} ({1!r}) has refcount {2:n}, not 1+".format(
  407. ident, self.id_to_obj[ident],
  408. self.id_to_refcount[ident]))
  409. self.id_to_refcount[ident] -= 1
  410. if self.id_to_refcount[ident] == 0:
  411. del self.id_to_refcount[ident]
  412. if ident not in self.id_to_refcount:
  413. # Two-step process in case the object turns out to contain other
  414. # proxy objects (e.g. a managed list of managed lists).
  415. # Otherwise, deleting self.id_to_obj[ident] would trigger the
  416. # deleting of the stored value (another managed object) which would
  417. # in turn attempt to acquire the mutex that is already held here.
  418. self.id_to_obj[ident] = (None, (), None) # thread-safe
  419. util.debug('disposing of obj with id %r', ident)
  420. with self.mutex:
  421. del self.id_to_obj[ident]
  422. #
  423. # Class to represent state of a manager
  424. #
  425. class State(object):
  426. __slots__ = ['value']
  427. INITIAL = 0
  428. STARTED = 1
  429. SHUTDOWN = 2
  430. #
  431. # Mapping from serializer name to Listener and Client types
  432. #
  433. listener_client = {
  434. 'pickle' : (connection.Listener, connection.Client),
  435. 'xmlrpclib' : (connection.XmlListener, connection.XmlClient)
  436. }
  437. #
  438. # Definition of BaseManager
  439. #
  440. class BaseManager(object):
  441. '''
  442. Base class for managers
  443. '''
  444. _registry = {}
  445. _Server = Server
  446. def __init__(self, address=None, authkey=None, serializer='pickle',
  447. ctx=None):
  448. if authkey is None:
  449. authkey = process.current_process().authkey
  450. self._address = address # XXX not final address if eg ('', 0)
  451. self._authkey = process.AuthenticationString(authkey)
  452. self._state = State()
  453. self._state.value = State.INITIAL
  454. self._serializer = serializer
  455. self._Listener, self._Client = listener_client[serializer]
  456. self._ctx = ctx or get_context()
  457. def get_server(self):
  458. '''
  459. Return server object with serve_forever() method and address attribute
  460. '''
  461. if self._state.value != State.INITIAL:
  462. if self._state.value == State.STARTED:
  463. raise ProcessError("Already started server")
  464. elif self._state.value == State.SHUTDOWN:
  465. raise ProcessError("Manager has shut down")
  466. else:
  467. raise ProcessError(
  468. "Unknown state {!r}".format(self._state.value))
  469. return Server(self._registry, self._address,
  470. self._authkey, self._serializer)
  471. def connect(self):
  472. '''
  473. Connect manager object to the server process
  474. '''
  475. Listener, Client = listener_client[self._serializer]
  476. conn = Client(self._address, authkey=self._authkey)
  477. dispatch(conn, None, 'dummy')
  478. self._state.value = State.STARTED
  479. def start(self, initializer=None, initargs=()):
  480. '''
  481. Spawn a server process for this manager object
  482. '''
  483. if self._state.value != State.INITIAL:
  484. if self._state.value == State.STARTED:
  485. raise ProcessError("Already started server")
  486. elif self._state.value == State.SHUTDOWN:
  487. raise ProcessError("Manager has shut down")
  488. else:
  489. raise ProcessError(
  490. "Unknown state {!r}".format(self._state.value))
  491. if initializer is not None and not callable(initializer):
  492. raise TypeError('initializer must be a callable')
  493. # pipe over which we will retrieve address of server
  494. reader, writer = connection.Pipe(duplex=False)
  495. # spawn process which runs a server
  496. self._process = self._ctx.Process(
  497. target=type(self)._run_server,
  498. args=(self._registry, self._address, self._authkey,
  499. self._serializer, writer, initializer, initargs),
  500. )
  501. ident = ':'.join(str(i) for i in self._process._identity)
  502. self._process.name = type(self).__name__ + '-' + ident
  503. self._process.start()
  504. # get address of server
  505. writer.close()
  506. self._address = reader.recv()
  507. reader.close()
  508. # register a finalizer
  509. self._state.value = State.STARTED
  510. self.shutdown = util.Finalize(
  511. self, type(self)._finalize_manager,
  512. args=(self._process, self._address, self._authkey,
  513. self._state, self._Client),
  514. exitpriority=0
  515. )
  516. @classmethod
  517. def _run_server(cls, registry, address, authkey, serializer, writer,
  518. initializer=None, initargs=()):
  519. '''
  520. Create a server, report its address and run it
  521. '''
  522. # bpo-36368: protect server process from KeyboardInterrupt signals
  523. signal.signal(signal.SIGINT, signal.SIG_IGN)
  524. if initializer is not None:
  525. initializer(*initargs)
  526. # create server
  527. server = cls._Server(registry, address, authkey, serializer)
  528. # inform parent process of the server's address
  529. writer.send(server.address)
  530. writer.close()
  531. # run the manager
  532. util.info('manager serving at %r', server.address)
  533. server.serve_forever()
  534. def _create(self, typeid, /, *args, **kwds):
  535. '''
  536. Create a new shared object; return the token and exposed tuple
  537. '''
  538. assert self._state.value == State.STARTED, 'server not yet started'
  539. conn = self._Client(self._address, authkey=self._authkey)
  540. try:
  541. id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds)
  542. finally:
  543. conn.close()
  544. return Token(typeid, self._address, id), exposed
  545. def join(self, timeout=None):
  546. '''
  547. Join the manager process (if it has been spawned)
  548. '''
  549. if self._process is not None:
  550. self._process.join(timeout)
  551. if not self._process.is_alive():
  552. self._process = None
  553. def _debug_info(self):
  554. '''
  555. Return some info about the servers shared objects and connections
  556. '''
  557. conn = self._Client(self._address, authkey=self._authkey)
  558. try:
  559. return dispatch(conn, None, 'debug_info')
  560. finally:
  561. conn.close()
  562. def _number_of_objects(self):
  563. '''
  564. Return the number of shared objects
  565. '''
  566. conn = self._Client(self._address, authkey=self._authkey)
  567. try:
  568. return dispatch(conn, None, 'number_of_objects')
  569. finally:
  570. conn.close()
  571. def __enter__(self):
  572. if self._state.value == State.INITIAL:
  573. self.start()
  574. if self._state.value != State.STARTED:
  575. if self._state.value == State.INITIAL:
  576. raise ProcessError("Unable to start server")
  577. elif self._state.value == State.SHUTDOWN:
  578. raise ProcessError("Manager has shut down")
  579. else:
  580. raise ProcessError(
  581. "Unknown state {!r}".format(self._state.value))
  582. return self
  583. def __exit__(self, exc_type, exc_val, exc_tb):
  584. self.shutdown()
  585. @staticmethod
  586. def _finalize_manager(process, address, authkey, state, _Client):
  587. '''
  588. Shutdown the manager process; will be registered as a finalizer
  589. '''
  590. if process.is_alive():
  591. util.info('sending shutdown message to manager')
  592. try:
  593. conn = _Client(address, authkey=authkey)
  594. try:
  595. dispatch(conn, None, 'shutdown')
  596. finally:
  597. conn.close()
  598. except Exception:
  599. pass
  600. process.join(timeout=1.0)
  601. if process.is_alive():
  602. util.info('manager still alive')
  603. if hasattr(process, 'terminate'):
  604. util.info('trying to `terminate()` manager process')
  605. process.terminate()
  606. process.join(timeout=0.1)
  607. if process.is_alive():
  608. util.info('manager still alive after terminate')
  609. state.value = State.SHUTDOWN
  610. try:
  611. del BaseProxy._address_to_local[address]
  612. except KeyError:
  613. pass
  614. @property
  615. def address(self):
  616. return self._address
  617. @classmethod
  618. def register(cls, typeid, callable=None, proxytype=None, exposed=None,
  619. method_to_typeid=None, create_method=True):
  620. '''
  621. Register a typeid with the manager type
  622. '''
  623. if '_registry' not in cls.__dict__:
  624. cls._registry = cls._registry.copy()
  625. if proxytype is None:
  626. proxytype = AutoProxy
  627. exposed = exposed or getattr(proxytype, '_exposed_', None)
  628. method_to_typeid = method_to_typeid or \
  629. getattr(proxytype, '_method_to_typeid_', None)
  630. if method_to_typeid:
  631. for key, value in list(method_to_typeid.items()): # isinstance?
  632. assert type(key) is str, '%r is not a string' % key
  633. assert type(value) is str, '%r is not a string' % value
  634. cls._registry[typeid] = (
  635. callable, exposed, method_to_typeid, proxytype
  636. )
  637. if create_method:
  638. def temp(self, /, *args, **kwds):
  639. util.debug('requesting creation of a shared %r object', typeid)
  640. token, exp = self._create(typeid, *args, **kwds)
  641. proxy = proxytype(
  642. token, self._serializer, manager=self,
  643. authkey=self._authkey, exposed=exp
  644. )
  645. conn = self._Client(token.address, authkey=self._authkey)
  646. dispatch(conn, None, 'decref', (token.id,))
  647. return proxy
  648. temp.__name__ = typeid
  649. setattr(cls, typeid, temp)
  650. #
  651. # Subclass of set which get cleared after a fork
  652. #
  653. class ProcessLocalSet(set):
  654. def __init__(self):
  655. util.register_after_fork(self, lambda obj: obj.clear())
  656. def __reduce__(self):
  657. return type(self), ()
  658. #
  659. # Definition of BaseProxy
  660. #
  661. class BaseProxy(object):
  662. '''
  663. A base for proxies of shared objects
  664. '''
  665. _address_to_local = {}
  666. _mutex = util.ForkAwareThreadLock()
  667. def __init__(self, token, serializer, manager=None,
  668. authkey=None, exposed=None, incref=True, manager_owned=False):
  669. with BaseProxy._mutex:
  670. tls_idset = BaseProxy._address_to_local.get(token.address, None)
  671. if tls_idset is None:
  672. tls_idset = util.ForkAwareLocal(), ProcessLocalSet()
  673. BaseProxy._address_to_local[token.address] = tls_idset
  674. # self._tls is used to record the connection used by this
  675. # thread to communicate with the manager at token.address
  676. self._tls = tls_idset[0]
  677. # self._idset is used to record the identities of all shared
  678. # objects for which the current process owns references and
  679. # which are in the manager at token.address
  680. self._idset = tls_idset[1]
  681. self._token = token
  682. self._id = self._token.id
  683. self._manager = manager
  684. self._serializer = serializer
  685. self._Client = listener_client[serializer][1]
  686. # Should be set to True only when a proxy object is being created
  687. # on the manager server; primary use case: nested proxy objects.
  688. # RebuildProxy detects when a proxy is being created on the manager
  689. # and sets this value appropriately.
  690. self._owned_by_manager = manager_owned
  691. if authkey is not None:
  692. self._authkey = process.AuthenticationString(authkey)
  693. elif self._manager is not None:
  694. self._authkey = self._manager._authkey
  695. else:
  696. self._authkey = process.current_process().authkey
  697. if incref:
  698. self._incref()
  699. util.register_after_fork(self, BaseProxy._after_fork)
  700. def _connect(self):
  701. util.debug('making connection to manager')
  702. name = process.current_process().name
  703. if threading.current_thread().name != 'MainThread':
  704. name += '|' + threading.current_thread().name
  705. conn = self._Client(self._token.address, authkey=self._authkey)
  706. dispatch(conn, None, 'accept_connection', (name,))
  707. self._tls.connection = conn
  708. def _callmethod(self, methodname, args=(), kwds={}):
  709. '''
  710. Try to call a method of the referrent and return a copy of the result
  711. '''
  712. try:
  713. conn = self._tls.connection
  714. except AttributeError:
  715. util.debug('thread %r does not own a connection',
  716. threading.current_thread().name)
  717. self._connect()
  718. conn = self._tls.connection
  719. conn.send((self._id, methodname, args, kwds))
  720. kind, result = conn.recv()
  721. if kind == '#RETURN':
  722. return result
  723. elif kind == '#PROXY':
  724. exposed, token = result
  725. proxytype = self._manager._registry[token.typeid][-1]
  726. token.address = self._token.address
  727. proxy = proxytype(
  728. token, self._serializer, manager=self._manager,
  729. authkey=self._authkey, exposed=exposed
  730. )
  731. conn = self._Client(token.address, authkey=self._authkey)
  732. dispatch(conn, None, 'decref', (token.id,))
  733. return proxy
  734. raise convert_to_error(kind, result)
  735. def _getvalue(self):
  736. '''
  737. Get a copy of the value of the referent
  738. '''
  739. return self._callmethod('#GETVALUE')
  740. def _incref(self):
  741. if self._owned_by_manager:
  742. util.debug('owned_by_manager skipped INCREF of %r', self._token.id)
  743. return
  744. conn = self._Client(self._token.address, authkey=self._authkey)
  745. dispatch(conn, None, 'incref', (self._id,))
  746. util.debug('INCREF %r', self._token.id)
  747. self._idset.add(self._id)
  748. state = self._manager and self._manager._state
  749. self._close = util.Finalize(
  750. self, BaseProxy._decref,
  751. args=(self._token, self._authkey, state,
  752. self._tls, self._idset, self._Client),
  753. exitpriority=10
  754. )
  755. @staticmethod
  756. def _decref(token, authkey, state, tls, idset, _Client):
  757. idset.discard(token.id)
  758. # check whether manager is still alive
  759. if state is None or state.value == State.STARTED:
  760. # tell manager this process no longer cares about referent
  761. try:
  762. util.debug('DECREF %r', token.id)
  763. conn = _Client(token.address, authkey=authkey)
  764. dispatch(conn, None, 'decref', (token.id,))
  765. except Exception as e:
  766. util.debug('... decref failed %s', e)
  767. else:
  768. util.debug('DECREF %r -- manager already shutdown', token.id)
  769. # check whether we can close this thread's connection because
  770. # the process owns no more references to objects for this manager
  771. if not idset and hasattr(tls, 'connection'):
  772. util.debug('thread %r has no more proxies so closing conn',
  773. threading.current_thread().name)
  774. tls.connection.close()
  775. del tls.connection
  776. def _after_fork(self):
  777. self._manager = None
  778. try:
  779. self._incref()
  780. except Exception as e:
  781. # the proxy may just be for a manager which has shutdown
  782. util.info('incref failed: %s' % e)
  783. def __reduce__(self):
  784. kwds = {}
  785. if get_spawning_popen() is not None:
  786. kwds['authkey'] = self._authkey
  787. if getattr(self, '_isauto', False):
  788. kwds['exposed'] = self._exposed_
  789. return (RebuildProxy,
  790. (AutoProxy, self._token, self._serializer, kwds))
  791. else:
  792. return (RebuildProxy,
  793. (type(self), self._token, self._serializer, kwds))
  794. def __deepcopy__(self, memo):
  795. return self._getvalue()
  796. def __repr__(self):
  797. return '<%s object, typeid %r at %#x>' % \
  798. (type(self).__name__, self._token.typeid, id(self))
  799. def __str__(self):
  800. '''
  801. Return representation of the referent (or a fall-back if that fails)
  802. '''
  803. try:
  804. return self._callmethod('__repr__')
  805. except Exception:
  806. return repr(self)[:-1] + "; '__str__()' failed>"
  807. #
  808. # Function used for unpickling
  809. #
  810. def RebuildProxy(func, token, serializer, kwds):
  811. '''
  812. Function used for unpickling proxy objects.
  813. '''
  814. server = getattr(process.current_process(), '_manager_server', None)
  815. if server and server.address == token.address:
  816. util.debug('Rebuild a proxy owned by manager, token=%r', token)
  817. kwds['manager_owned'] = True
  818. if token.id not in server.id_to_local_proxy_obj:
  819. server.id_to_local_proxy_obj[token.id] = \
  820. server.id_to_obj[token.id]
  821. incref = (
  822. kwds.pop('incref', True) and
  823. not getattr(process.current_process(), '_inheriting', False)
  824. )
  825. return func(token, serializer, incref=incref, **kwds)
  826. #
  827. # Functions to create proxies and proxy types
  828. #
  829. def MakeProxyType(name, exposed, _cache={}):
  830. '''
  831. Return a proxy type whose methods are given by `exposed`
  832. '''
  833. exposed = tuple(exposed)
  834. try:
  835. return _cache[(name, exposed)]
  836. except KeyError:
  837. pass
  838. dic = {}
  839. for meth in exposed:
  840. exec('''def %s(self, /, *args, **kwds):
  841. return self._callmethod(%r, args, kwds)''' % (meth, meth), dic)
  842. ProxyType = type(name, (BaseProxy,), dic)
  843. ProxyType._exposed_ = exposed
  844. _cache[(name, exposed)] = ProxyType
  845. return ProxyType
  846. def AutoProxy(token, serializer, manager=None, authkey=None,
  847. exposed=None, incref=True):
  848. '''
  849. Return an auto-proxy for `token`
  850. '''
  851. _Client = listener_client[serializer][1]
  852. if exposed is None:
  853. conn = _Client(token.address, authkey=authkey)
  854. try:
  855. exposed = dispatch(conn, None, 'get_methods', (token,))
  856. finally:
  857. conn.close()
  858. if authkey is None and manager is not None:
  859. authkey = manager._authkey
  860. if authkey is None:
  861. authkey = process.current_process().authkey
  862. ProxyType = MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
  863. proxy = ProxyType(token, serializer, manager=manager, authkey=authkey,
  864. incref=incref)
  865. proxy._isauto = True
  866. return proxy
  867. #
  868. # Types/callables which we will register with SyncManager
  869. #
  870. class Namespace(object):
  871. def __init__(self, /, **kwds):
  872. self.__dict__.update(kwds)
  873. def __repr__(self):
  874. items = list(self.__dict__.items())
  875. temp = []
  876. for name, value in items:
  877. if not name.startswith('_'):
  878. temp.append('%s=%r' % (name, value))
  879. temp.sort()
  880. return '%s(%s)' % (self.__class__.__name__, ', '.join(temp))
  881. class Value(object):
  882. def __init__(self, typecode, value, lock=True):
  883. self._typecode = typecode
  884. self._value = value
  885. def get(self):
  886. return self._value
  887. def set(self, value):
  888. self._value = value
  889. def __repr__(self):
  890. return '%s(%r, %r)'%(type(self).__name__, self._typecode, self._value)
  891. value = property(get, set)
  892. def Array(typecode, sequence, lock=True):
  893. return array.array(typecode, sequence)
  894. #
  895. # Proxy types used by SyncManager
  896. #
  897. class IteratorProxy(BaseProxy):
  898. _exposed_ = ('__next__', 'send', 'throw', 'close')
  899. def __iter__(self):
  900. return self
  901. def __next__(self, *args):
  902. return self._callmethod('__next__', args)
  903. def send(self, *args):
  904. return self._callmethod('send', args)
  905. def throw(self, *args):
  906. return self._callmethod('throw', args)
  907. def close(self, *args):
  908. return self._callmethod('close', args)
  909. class AcquirerProxy(BaseProxy):
  910. _exposed_ = ('acquire', 'release')
  911. def acquire(self, blocking=True, timeout=None):
  912. args = (blocking,) if timeout is None else (blocking, timeout)
  913. return self._callmethod('acquire', args)
  914. def release(self):
  915. return self._callmethod('release')
  916. def __enter__(self):
  917. return self._callmethod('acquire')
  918. def __exit__(self, exc_type, exc_val, exc_tb):
  919. return self._callmethod('release')
  920. class ConditionProxy(AcquirerProxy):
  921. _exposed_ = ('acquire', 'release', 'wait', 'notify', 'notify_all')
  922. def wait(self, timeout=None):
  923. return self._callmethod('wait', (timeout,))
  924. def notify(self, n=1):
  925. return self._callmethod('notify', (n,))
  926. def notify_all(self):
  927. return self._callmethod('notify_all')
  928. def wait_for(self, predicate, timeout=None):
  929. result = predicate()
  930. if result:
  931. return result
  932. if timeout is not None:
  933. endtime = time.monotonic() + timeout
  934. else:
  935. endtime = None
  936. waittime = None
  937. while not result:
  938. if endtime is not None:
  939. waittime = endtime - time.monotonic()
  940. if waittime <= 0:
  941. break
  942. self.wait(waittime)
  943. result = predicate()
  944. return result
  945. class EventProxy(BaseProxy):
  946. _exposed_ = ('is_set', 'set', 'clear', 'wait')
  947. def is_set(self):
  948. return self._callmethod('is_set')
  949. def set(self):
  950. return self._callmethod('set')
  951. def clear(self):
  952. return self._callmethod('clear')
  953. def wait(self, timeout=None):
  954. return self._callmethod('wait', (timeout,))
  955. class BarrierProxy(BaseProxy):
  956. _exposed_ = ('__getattribute__', 'wait', 'abort', 'reset')
  957. def wait(self, timeout=None):
  958. return self._callmethod('wait', (timeout,))
  959. def abort(self):
  960. return self._callmethod('abort')
  961. def reset(self):
  962. return self._callmethod('reset')
  963. @property
  964. def parties(self):
  965. return self._callmethod('__getattribute__', ('parties',))
  966. @property
  967. def n_waiting(self):
  968. return self._callmethod('__getattribute__', ('n_waiting',))
  969. @property
  970. def broken(self):
  971. return self._callmethod('__getattribute__', ('broken',))
  972. class NamespaceProxy(BaseProxy):
  973. _exposed_ = ('__getattribute__', '__setattr__', '__delattr__')
  974. def __getattr__(self, key):
  975. if key[0] == '_':
  976. return object.__getattribute__(self, key)
  977. callmethod = object.__getattribute__(self, '_callmethod')
  978. return callmethod('__getattribute__', (key,))
  979. def __setattr__(self, key, value):
  980. if key[0] == '_':
  981. return object.__setattr__(self, key, value)
  982. callmethod = object.__getattribute__(self, '_callmethod')
  983. return callmethod('__setattr__', (key, value))
  984. def __delattr__(self, key):
  985. if key[0] == '_':
  986. return object.__delattr__(self, key)
  987. callmethod = object.__getattribute__(self, '_callmethod')
  988. return callmethod('__delattr__', (key,))
  989. class ValueProxy(BaseProxy):
  990. _exposed_ = ('get', 'set')
  991. def get(self):
  992. return self._callmethod('get')
  993. def set(self, value):
  994. return self._callmethod('set', (value,))
  995. value = property(get, set)
  996. BaseListProxy = MakeProxyType('BaseListProxy', (
  997. '__add__', '__contains__', '__delitem__', '__getitem__', '__len__',
  998. '__mul__', '__reversed__', '__rmul__', '__setitem__',
  999. 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
  1000. 'reverse', 'sort', '__imul__'
  1001. ))
  1002. class ListProxy(BaseListProxy):
  1003. def __iadd__(self, value):
  1004. self._callmethod('extend', (value,))
  1005. return self
  1006. def __imul__(self, value):
  1007. self._callmethod('__imul__', (value,))
  1008. return self
  1009. DictProxy = MakeProxyType('DictProxy', (
  1010. '__contains__', '__delitem__', '__getitem__', '__iter__', '__len__',
  1011. '__setitem__', 'clear', 'copy', 'get', 'items',
  1012. 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'
  1013. ))
  1014. DictProxy._method_to_typeid_ = {
  1015. '__iter__': 'Iterator',
  1016. }
  1017. ArrayProxy = MakeProxyType('ArrayProxy', (
  1018. '__len__', '__getitem__', '__setitem__'
  1019. ))
  1020. BasePoolProxy = MakeProxyType('PoolProxy', (
  1021. 'apply', 'apply_async', 'close', 'imap', 'imap_unordered', 'join',
  1022. 'map', 'map_async', 'starmap', 'starmap_async', 'terminate',
  1023. ))
  1024. BasePoolProxy._method_to_typeid_ = {
  1025. 'apply_async': 'AsyncResult',
  1026. 'map_async': 'AsyncResult',
  1027. 'starmap_async': 'AsyncResult',
  1028. 'imap': 'Iterator',
  1029. 'imap_unordered': 'Iterator'
  1030. }
  1031. class PoolProxy(BasePoolProxy):
  1032. def __enter__(self):
  1033. return self
  1034. def __exit__(self, exc_type, exc_val, exc_tb):
  1035. self.terminate()
  1036. #
  1037. # Definition of SyncManager
  1038. #
  1039. class SyncManager(BaseManager):
  1040. '''
  1041. Subclass of `BaseManager` which supports a number of shared object types.
  1042. The types registered are those intended for the synchronization
  1043. of threads, plus `dict`, `list` and `Namespace`.
  1044. The `multiprocessing.Manager()` function creates started instances of
  1045. this class.
  1046. '''
  1047. SyncManager.register('Queue', queue.Queue)
  1048. SyncManager.register('JoinableQueue', queue.Queue)
  1049. SyncManager.register('Event', threading.Event, EventProxy)
  1050. SyncManager.register('Lock', threading.Lock, AcquirerProxy)
  1051. SyncManager.register('RLock', threading.RLock, AcquirerProxy)
  1052. SyncManager.register('Semaphore', threading.Semaphore, AcquirerProxy)
  1053. SyncManager.register('BoundedSemaphore', threading.BoundedSemaphore,
  1054. AcquirerProxy)
  1055. SyncManager.register('Condition', threading.Condition, ConditionProxy)
  1056. SyncManager.register('Barrier', threading.Barrier, BarrierProxy)
  1057. SyncManager.register('Pool', pool.Pool, PoolProxy)
  1058. SyncManager.register('list', list, ListProxy)
  1059. SyncManager.register('dict', dict, DictProxy)
  1060. SyncManager.register('Value', Value, ValueProxy)
  1061. SyncManager.register('Array', Array, ArrayProxy)
  1062. SyncManager.register('Namespace', Namespace, NamespaceProxy)
  1063. # types returned by methods of PoolProxy
  1064. SyncManager.register('Iterator', proxytype=IteratorProxy, create_method=False)
  1065. SyncManager.register('AsyncResult', create_method=False)
  1066. #
  1067. # Definition of SharedMemoryManager and SharedMemoryServer
  1068. #
  1069. if HAS_SHMEM:
  1070. class _SharedMemoryTracker:
  1071. "Manages one or more shared memory segments."
  1072. def __init__(self, name, segment_names=[]):
  1073. self.shared_memory_context_name = name
  1074. self.segment_names = segment_names
  1075. def register_segment(self, segment_name):
  1076. "Adds the supplied shared memory block name to tracker."
  1077. util.debug(f"Register segment {segment_name!r} in pid {getpid()}")
  1078. self.segment_names.append(segment_name)
  1079. def destroy_segment(self, segment_name):
  1080. """Calls unlink() on the shared memory block with the supplied name
  1081. and removes it from the list of blocks being tracked."""
  1082. util.debug(f"Destroy segment {segment_name!r} in pid {getpid()}")
  1083. self.segment_names.remove(segment_name)
  1084. segment = shared_memory.SharedMemory(segment_name)
  1085. segment.close()
  1086. segment.unlink()
  1087. def unlink(self):
  1088. "Calls destroy_segment() on all tracked shared memory blocks."
  1089. for segment_name in self.segment_names[:]:
  1090. self.destroy_segment(segment_name)
  1091. def __del__(self):
  1092. util.debug(f"Call {self.__class__.__name__}.__del__ in {getpid()}")
  1093. self.unlink()
  1094. def __getstate__(self):
  1095. return (self.shared_memory_context_name, self.segment_names)
  1096. def __setstate__(self, state):
  1097. self.__init__(*state)
  1098. class SharedMemoryServer(Server):
  1099. public = Server.public + \
  1100. ['track_segment', 'release_segment', 'list_segments']
  1101. def __init__(self, *args, **kwargs):
  1102. Server.__init__(self, *args, **kwargs)
  1103. self.shared_memory_context = \
  1104. _SharedMemoryTracker(f"shmm_{self.address}_{getpid()}")
  1105. util.debug(f"SharedMemoryServer started by pid {getpid()}")
  1106. def create(*args, **kwargs):
  1107. """Create a new distributed-shared object (not backed by a shared
  1108. memory block) and return its id to be used in a Proxy Object."""
  1109. # Unless set up as a shared proxy, don't make shared_memory_context
  1110. # a standard part of kwargs. This makes things easier for supplying
  1111. # simple functions.
  1112. if len(args) >= 3:
  1113. typeod = args[2]
  1114. elif 'typeid' in kwargs:
  1115. typeid = kwargs['typeid']
  1116. elif not args:
  1117. raise TypeError("descriptor 'create' of 'SharedMemoryServer' "
  1118. "object needs an argument")
  1119. else:
  1120. raise TypeError('create expected at least 2 positional '
  1121. 'arguments, got %d' % (len(args)-1))
  1122. if hasattr(self.registry[typeid][-1], "_shared_memory_proxy"):
  1123. kwargs['shared_memory_context'] = self.shared_memory_context
  1124. return Server.create(*args, **kwargs)
  1125. create.__text_signature__ = '($self, c, typeid, /, *args, **kwargs)'
  1126. def shutdown(self, c):
  1127. "Call unlink() on all tracked shared memory, terminate the Server."
  1128. self.shared_memory_context.unlink()
  1129. return Server.shutdown(self, c)
  1130. def track_segment(self, c, segment_name):
  1131. "Adds the supplied shared memory block name to Server's tracker."
  1132. self.shared_memory_context.register_segment(segment_name)
  1133. def release_segment(self, c, segment_name):
  1134. """Calls unlink() on the shared memory block with the supplied name
  1135. and removes it from the tracker instance inside the Server."""
  1136. self.shared_memory_context.destroy_segment(segment_name)
  1137. def list_segments(self, c):
  1138. """Returns a list of names of shared memory blocks that the Server
  1139. is currently tracking."""
  1140. return self.shared_memory_context.segment_names
  1141. class SharedMemoryManager(BaseManager):
  1142. """Like SyncManager but uses SharedMemoryServer instead of Server.
  1143. It provides methods for creating and returning SharedMemory instances
  1144. and for creating a list-like object (ShareableList) backed by shared
  1145. memory. It also provides methods that create and return Proxy Objects
  1146. that support synchronization across processes (i.e. multi-process-safe
  1147. locks and semaphores).
  1148. """
  1149. _Server = SharedMemoryServer
  1150. def __init__(self, *args, **kwargs):
  1151. if os.name == "posix":
  1152. # bpo-36867: Ensure the resource_tracker is running before
  1153. # launching the manager process, so that concurrent
  1154. # shared_memory manipulation both in the manager and in the
  1155. # current process does not create two resource_tracker
  1156. # processes.
  1157. from . import resource_tracker
  1158. resource_tracker.ensure_running()
  1159. BaseManager.__init__(self, *args, **kwargs)
  1160. util.debug(f"{self.__class__.__name__} created by pid {getpid()}")
  1161. def __del__(self):
  1162. util.debug(f"{self.__class__.__name__}.__del__ by pid {getpid()}")
  1163. pass
  1164. def get_server(self):
  1165. 'Better than monkeypatching for now; merge into Server ultimately'
  1166. if self._state.value != State.INITIAL:
  1167. if self._state.value == State.STARTED:
  1168. raise ProcessError("Already started SharedMemoryServer")
  1169. elif self._state.value == State.SHUTDOWN:
  1170. raise ProcessError("SharedMemoryManager has shut down")
  1171. else:
  1172. raise ProcessError(
  1173. "Unknown state {!r}".format(self._state.value))
  1174. return self._Server(self._registry, self._address,
  1175. self._authkey, self._serializer)
  1176. def SharedMemory(self, size):
  1177. """Returns a new SharedMemory instance with the specified size in
  1178. bytes, to be tracked by the manager."""
  1179. with self._Client(self._address, authkey=self._authkey) as conn:
  1180. sms = shared_memory.SharedMemory(None, create=True, size=size)
  1181. try:
  1182. dispatch(conn, None, 'track_segment', (sms.name,))
  1183. except BaseException as e:
  1184. sms.unlink()
  1185. raise e
  1186. return sms
  1187. def ShareableList(self, sequence):
  1188. """Returns a new ShareableList instance populated with the values
  1189. from the input sequence, to be tracked by the manager."""
  1190. with self._Client(self._address, authkey=self._authkey) as conn:
  1191. sl = shared_memory.ShareableList(sequence)
  1192. try:
  1193. dispatch(conn, None, 'track_segment', (sl.shm.name,))
  1194. except BaseException as e:
  1195. sl.shm.unlink()
  1196. raise e
  1197. return sl