@@ -3793,13 +3793,6 @@ def test_shared_memory_basics(self):
3793
3793
self .assertIn (sms .name , str (sms ))
3794
3794
self .assertIn (str (sms .size ), str (sms ))
3795
3795
3796
- # Test pickling
3797
- sms .buf [0 :6 ] = b'pickle'
3798
- pickled_sms = pickle .dumps (sms )
3799
- sms2 = pickle .loads (pickled_sms )
3800
- self .assertEqual (sms .name , sms2 .name )
3801
- self .assertEqual (bytes (sms .buf [0 :6 ]), bytes (sms2 .buf [0 :6 ]), b'pickle' )
3802
-
3803
3796
# Modify contents of shared memory segment through memoryview.
3804
3797
sms .buf [0 ] = 42
3805
3798
self .assertEqual (sms .buf [0 ], 42 )
@@ -3898,6 +3891,29 @@ class OptionalAttachSharedMemory(shared_memory.SharedMemory):
3898
3891
3899
3892
sms .close ()
3900
3893
3894
+ def test_shared_memory_recreate (self ):
3895
+ # Test if shared memory segment is created properly,
3896
+ # when _make_filename returns an existing shared memory segment name
3897
+ with unittest .mock .patch (
3898
+ 'multiprocessing.shared_memory._make_filename' ) as mock_make_filename :
3899
+
3900
+ NAME_PREFIX = shared_memory ._SHM_NAME_PREFIX
3901
+ names = ['test01_fn' , 'test02_fn' ]
3902
+ # Prepend NAME_PREFIX which can be '/psm_' or 'wnsm_', necessary
3903
+ # because some POSIX compliant systems require name to start with /
3904
+ names = [NAME_PREFIX + name for name in names ]
3905
+
3906
+ mock_make_filename .side_effect = names
3907
+ shm1 = shared_memory .SharedMemory (create = True , size = 1 )
3908
+ self .addCleanup (shm1 .unlink )
3909
+ self .assertEqual (shm1 ._name , names [0 ])
3910
+
3911
+ mock_make_filename .side_effect = names
3912
+ shm2 = shared_memory .SharedMemory (create = True , size = 1 )
3913
+ self .addCleanup (shm2 .unlink )
3914
+ self .assertEqual (shm2 ._name , names [1 ])
3915
+
3916
+ def test_invalid_shared_memory_cration (self ):
3901
3917
# Test creating a shared memory segment with negative size
3902
3918
with self .assertRaises (ValueError ):
3903
3919
sms_invalid = shared_memory .SharedMemory (create = True , size = - 1 )
@@ -3910,6 +3926,47 @@ class OptionalAttachSharedMemory(shared_memory.SharedMemory):
3910
3926
with self .assertRaises (ValueError ):
3911
3927
sms_invalid = shared_memory .SharedMemory (create = True )
3912
3928
3929
+ def test_shared_memory_pickle_unpickle (self ):
3930
+ for proto in range (pickle .HIGHEST_PROTOCOL + 1 ):
3931
+ with self .subTest (proto = proto ):
3932
+ sms = shared_memory .SharedMemory (create = True , size = 512 )
3933
+ self .addCleanup (sms .unlink )
3934
+ sms .buf [0 :6 ] = b'pickle'
3935
+
3936
+ # Test pickling
3937
+ pickled_sms = pickle .dumps (sms , protocol = proto )
3938
+
3939
+ # Test unpickling
3940
+ sms2 = pickle .loads (pickled_sms )
3941
+ self .assertIsInstance (sms2 , shared_memory .SharedMemory )
3942
+ self .assertEqual (sms .name , sms2 .name )
3943
+ self .assertEqual (bytes (sms .buf [0 :6 ]), b'pickle' )
3944
+ self .assertEqual (bytes (sms2 .buf [0 :6 ]), b'pickle' )
3945
+
3946
+ # Test that unpickled version is still the same SharedMemory
3947
+ sms .buf [0 :6 ] = b'newval'
3948
+ self .assertEqual (bytes (sms .buf [0 :6 ]), b'newval' )
3949
+ self .assertEqual (bytes (sms2 .buf [0 :6 ]), b'newval' )
3950
+
3951
+ sms2 .buf [0 :6 ] = b'oldval'
3952
+ self .assertEqual (bytes (sms .buf [0 :6 ]), b'oldval' )
3953
+ self .assertEqual (bytes (sms2 .buf [0 :6 ]), b'oldval' )
3954
+
3955
+ def test_shared_memory_pickle_unpickle_dead_object (self ):
3956
+ for proto in range (pickle .HIGHEST_PROTOCOL + 1 ):
3957
+ with self .subTest (proto = proto ):
3958
+ sms = shared_memory .SharedMemory (create = True , size = 512 )
3959
+ sms .buf [0 :6 ] = b'pickle'
3960
+ pickled_sms = pickle .dumps (sms , protocol = proto )
3961
+
3962
+ # Now, we are going to kill the original object.
3963
+ # So, unpickled one won't be able to attach to it.
3964
+ sms .close ()
3965
+ sms .unlink ()
3966
+
3967
+ with self .assertRaises (FileNotFoundError ):
3968
+ pickle .loads (pickled_sms )
3969
+
3913
3970
def test_shared_memory_across_processes (self ):
3914
3971
# bpo-40135: don't define shared memory block's name in case of
3915
3972
# the failure when we run multiprocessing tests in parallel.
@@ -4127,29 +4184,45 @@ def test_shared_memory_ShareableList_basics(self):
4127
4184
empty_sl .shm .unlink ()
4128
4185
4129
4186
def test_shared_memory_ShareableList_pickling (self ):
4130
- sl = shared_memory .ShareableList (range (10 ))
4131
- self .addCleanup (sl .shm .unlink )
4132
-
4133
- serialized_sl = pickle .dumps (sl )
4134
- deserialized_sl = pickle .loads (serialized_sl )
4135
- self .assertTrue (
4136
- isinstance (deserialized_sl , shared_memory .ShareableList )
4137
- )
4138
- self .assertTrue (deserialized_sl [- 1 ], 9 )
4139
- self .assertFalse (sl is deserialized_sl )
4140
- deserialized_sl [4 ] = "changed"
4141
- self .assertEqual (sl [4 ], "changed" )
4142
-
4143
- # Verify data is not being put into the pickled representation.
4144
- name = 'a' * len (sl .shm .name )
4145
- larger_sl = shared_memory .ShareableList (range (400 ))
4146
- self .addCleanup (larger_sl .shm .unlink )
4147
- serialized_larger_sl = pickle .dumps (larger_sl )
4148
- self .assertTrue (len (serialized_sl ) == len (serialized_larger_sl ))
4149
- larger_sl .shm .close ()
4150
-
4151
- deserialized_sl .shm .close ()
4152
- sl .shm .close ()
4187
+ for proto in range (pickle .HIGHEST_PROTOCOL + 1 ):
4188
+ with self .subTest (proto = proto ):
4189
+ sl = shared_memory .ShareableList (range (10 ))
4190
+ self .addCleanup (sl .shm .unlink )
4191
+
4192
+ serialized_sl = pickle .dumps (sl , protocol = proto )
4193
+ deserialized_sl = pickle .loads (serialized_sl )
4194
+ self .assertIsInstance (
4195
+ deserialized_sl , shared_memory .ShareableList )
4196
+ self .assertEqual (deserialized_sl [- 1 ], 9 )
4197
+ self .assertIsNot (sl , deserialized_sl )
4198
+
4199
+ deserialized_sl [4 ] = "changed"
4200
+ self .assertEqual (sl [4 ], "changed" )
4201
+ sl [3 ] = "newvalue"
4202
+ self .assertEqual (deserialized_sl [3 ], "newvalue" )
4203
+
4204
+ larger_sl = shared_memory .ShareableList (range (400 ))
4205
+ self .addCleanup (larger_sl .shm .unlink )
4206
+ serialized_larger_sl = pickle .dumps (larger_sl , protocol = proto )
4207
+ self .assertEqual (len (serialized_sl ), len (serialized_larger_sl ))
4208
+ larger_sl .shm .close ()
4209
+
4210
+ deserialized_sl .shm .close ()
4211
+ sl .shm .close ()
4212
+
4213
+ def test_shared_memory_ShareableList_pickling_dead_object (self ):
4214
+ for proto in range (pickle .HIGHEST_PROTOCOL + 1 ):
4215
+ with self .subTest (proto = proto ):
4216
+ sl = shared_memory .ShareableList (range (10 ))
4217
+ serialized_sl = pickle .dumps (sl , protocol = proto )
4218
+
4219
+ # Now, we are going to kill the original object.
4220
+ # So, unpickled one won't be able to attach to it.
4221
+ sl .shm .close ()
4222
+ sl .shm .unlink ()
4223
+
4224
+ with self .assertRaises (FileNotFoundError ):
4225
+ pickle .loads (serialized_sl )
4153
4226
4154
4227
def test_shared_memory_cleaned_after_process_termination (self ):
4155
4228
cmd = '''if 1:
@@ -4202,7 +4275,7 @@ def test_shared_memory_cleaned_after_process_termination(self):
4202
4275
"shared_memory objects to clean up at shutdown" , err )
4203
4276
4204
4277
#
4205
- #
4278
+ # Test to verify that `Finalize` works.
4206
4279
#
4207
4280
4208
4281
class _TestFinalize (BaseTestCase ):
0 commit comments