Skip to content

Commit df276a3

Browse files
committed
Improve readability, add comments
1 parent 3d2ba4b commit df276a3

1 file changed

Lines changed: 36 additions & 32 deletions

File tree

Lib/multiprocessing/shared_memory.py

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ class ShareableList:
252252
packing format for any storable value must require no more than 8
253253
characters to describe its format."""
254254

255+
# The shared memory area is organized as follows:
256+
# - 8 bytes: number of items (N) as a 64-bit integer
257+
# - (N + 1) * 8 bytes: offsets of each element from the start of the
258+
# data area
259+
# - K bytes: the data area storing item values (with encoding and size
260+
# depending on their respective types)
261+
# - N * 8 bytes: `struct` format string for each element
262+
# - N bytes: index into _back_transforms_mapping for each element
263+
# (for reconstructing the corresponding Python value)
255264
_types_mapping = {
256265
int: "q",
257266
float: "d",
@@ -283,7 +292,8 @@ def _extract_recreation_code(value):
283292
return 3 # NoneType
284293

285294
def __init__(self, sequence=None, *, name=None):
286-
if sequence is not None:
295+
if name is None or sequence is not None:
296+
sequence = sequence or ()
287297
_formats = [
288298
self._types_mapping[type(item)]
289299
if not isinstance(item, (str, bytes))
@@ -295,10 +305,13 @@ def __init__(self, sequence=None, *, name=None):
295305
self._list_len = len(_formats)
296306
assert sum(len(fmt) <= 8 for fmt in _formats) == self._list_len
297307
offset = 0
298-
self._allocated_bytes = []
308+
# The offsets of each list element into the shared memory's
309+
# data area (0 meaning the start of the data area, not the start
310+
# of the shared memory area).
311+
self._allocated_offsets = [0]
299312
for fmt in _formats:
300313
offset += self._alignment if fmt[-1] != "s" else int(fmt[:-1])
301-
self._allocated_bytes.append(offset)
314+
self._allocated_offsets.append(offset)
302315
_recreation_codes = [
303316
self._extract_recreation_code(item) for item in sequence
304317
]
@@ -309,13 +322,9 @@ def __init__(self, sequence=None, *, name=None):
309322
self._format_back_transform_codes
310323
)
311324

325+
self.shm = SharedMemory(name, create=True, size=requested_size)
312326
else:
313-
requested_size = 8 # Some platforms require > 0.
314-
315-
if name is not None and sequence is None:
316327
self.shm = SharedMemory(name)
317-
else:
318-
self.shm = SharedMemory(name, create=True, size=requested_size)
319328

320329
if sequence is not None:
321330
_enc = _encoding
@@ -324,7 +333,7 @@ def __init__(self, sequence=None, *, name=None):
324333
self.shm.buf,
325334
0,
326335
self._list_len,
327-
*(self._allocated_bytes)
336+
*(self._allocated_offsets)
328337
)
329338
struct.pack_into(
330339
"".join(_formats),
@@ -347,7 +356,7 @@ def __init__(self, sequence=None, *, name=None):
347356

348357
else:
349358
self._list_len = len(self) # Obtains size from offset 0 in buffer.
350-
self._allocated_bytes = list(
359+
self._allocated_offsets = list(
351360
struct.unpack_from(
352361
self._format_size_metainfo,
353362
self.shm.buf,
@@ -374,7 +383,6 @@ def _get_packing_format(self, position):
374383
def _get_back_transform(self, position):
375384
"Gets the back transformation function for a single value."
376385

377-
position = position if position >= 0 else position + self._list_len
378386
if (position >= self._list_len) or (self._list_len < 0):
379387
raise IndexError("Requested position out of range.")
380388

@@ -391,7 +399,6 @@ def _set_packing_format_and_transform(self, position, fmt_as_str, value):
391399
"""Sets the packing format and back transformation code for a
392400
single value in the list at the specified position."""
393401

394-
position = position if position >= 0 else position + self._list_len
395402
if (position >= self._list_len) or (self._list_len < 0):
396403
raise IndexError("Requested position out of range.")
397404

@@ -411,12 +418,9 @@ def _set_packing_format_and_transform(self, position, fmt_as_str, value):
411418
)
412419

413420
def __getitem__(self, position):
421+
position = position if position >= 0 else position + self._list_len
414422
try:
415-
if position == 0:
416-
allocated_predecessor = 0
417-
else:
418-
allocated_predecessor = self._allocated_bytes[position - 1]
419-
offset = self._offset_data_start + allocated_predecessor
423+
offset = self._offset_data_start + self._allocated_offsets[position]
420424
(v,) = struct.unpack_from(
421425
self._get_packing_format(position),
422426
self.shm.buf,
@@ -431,28 +435,26 @@ def __getitem__(self, position):
431435
return v
432436

433437
def __setitem__(self, position, value):
438+
position = position if position >= 0 else position + self._list_len
434439
try:
435-
if position == 0:
436-
allocated_predecessor = 0
437-
else:
438-
allocated_predecessor = self._allocated_bytes[position - 1]
439-
offset = self._offset_data_start + allocated_predecessor
440+
item_offset = self._allocated_offsets[position]
441+
offset = self._offset_data_start + item_offset
440442
current_format = self._get_packing_format(position)
441443
except IndexError:
442444
raise IndexError("assignment index out of range")
443445

444446
if not isinstance(value, (str, bytes)):
445447
new_format = self._types_mapping[type(value)]
446448
else:
447-
allocated_position = self._allocated_bytes[position] - allocated_predecessor
449+
allocated_length = self._allocated_offsets[position + 1] - item_offset
448450

449-
if len(value) > allocated_position:
451+
if len(value) > allocated_length:
450452
raise ValueError("exceeds available storage for existing str")
451453
if current_format[-1] == "s":
452454
new_format = current_format
453455
else:
454456
new_format = self._types_mapping[str] % (
455-
allocated_position,
457+
allocated_length,
456458
)
457459

458460
self._set_packing_format_and_transform(
@@ -474,33 +476,35 @@ def __repr__(self):
474476

475477
@property
476478
def format(self):
477-
"The struct packing format used by all currently stored values."
479+
"The struct packing format used by all currently stored items."
478480
return "".join(
479481
self._get_packing_format(i) for i in range(self._list_len)
480482
)
481483

482484
@property
483485
def _format_size_metainfo(self):
484-
"The struct packing format used for metainfo on storage sizes."
485-
return f"{self._list_len}q"
486+
"The struct packing format used for the items' storage offsets."
487+
return "q" * (self._list_len + 1)
486488

487489
@property
488490
def _format_packing_metainfo(self):
489-
"The struct packing format used for the values' packing formats."
491+
"The struct packing format used for the items' packing formats."
490492
return "8s" * self._list_len
491493

492494
@property
493495
def _format_back_transform_codes(self):
494-
"The struct packing format used for the values' back transforms."
496+
"The struct packing format used for the items' back transforms."
495497
return "b" * self._list_len
496498

497499
@property
498500
def _offset_data_start(self):
499-
return (self._list_len + 1) * 8 # 8 bytes per "q"
501+
# - 8 bytes for the list length
502+
# - (N + 1) * 8 bytes for the element offsets
503+
return (self._list_len + 2) * 8
500504

501505
@property
502506
def _offset_packing_formats(self):
503-
return self._offset_data_start + self._allocated_bytes[-1]
507+
return self._offset_data_start + self._allocated_offsets[-1]
504508

505509
@property
506510
def _offset_back_transform_codes(self):

0 commit comments

Comments
 (0)