xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/multiprocessing/heap.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#
2*cda5da8dSAndroid Build Coastguard Worker# Module which supports allocation of memory from an mmap
3*cda5da8dSAndroid Build Coastguard Worker#
4*cda5da8dSAndroid Build Coastguard Worker# multiprocessing/heap.py
5*cda5da8dSAndroid Build Coastguard Worker#
6*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 2006-2008, R Oudkerk
7*cda5da8dSAndroid Build Coastguard Worker# Licensed to PSF under a Contributor Agreement.
8*cda5da8dSAndroid Build Coastguard Worker#
9*cda5da8dSAndroid Build Coastguard Worker
10*cda5da8dSAndroid Build Coastguard Workerimport bisect
11*cda5da8dSAndroid Build Coastguard Workerfrom collections import defaultdict
12*cda5da8dSAndroid Build Coastguard Workerimport mmap
13*cda5da8dSAndroid Build Coastguard Workerimport os
14*cda5da8dSAndroid Build Coastguard Workerimport sys
15*cda5da8dSAndroid Build Coastguard Workerimport tempfile
16*cda5da8dSAndroid Build Coastguard Workerimport threading
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard Workerfrom .context import reduction, assert_spawning
19*cda5da8dSAndroid Build Coastguard Workerfrom . import util
20*cda5da8dSAndroid Build Coastguard Worker
21*cda5da8dSAndroid Build Coastguard Worker__all__ = ['BufferWrapper']
22*cda5da8dSAndroid Build Coastguard Worker
23*cda5da8dSAndroid Build Coastguard Worker#
24*cda5da8dSAndroid Build Coastguard Worker# Inheritable class which wraps an mmap, and from which blocks can be allocated
25*cda5da8dSAndroid Build Coastguard Worker#
26*cda5da8dSAndroid Build Coastguard Worker
27*cda5da8dSAndroid Build Coastguard Workerif sys.platform == 'win32':
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard Worker    import _winapi
30*cda5da8dSAndroid Build Coastguard Worker
31*cda5da8dSAndroid Build Coastguard Worker    class Arena(object):
32*cda5da8dSAndroid Build Coastguard Worker        """
33*cda5da8dSAndroid Build Coastguard Worker        A shared memory area backed by anonymous memory (Windows).
34*cda5da8dSAndroid Build Coastguard Worker        """
35*cda5da8dSAndroid Build Coastguard Worker
36*cda5da8dSAndroid Build Coastguard Worker        _rand = tempfile._RandomNameSequence()
37*cda5da8dSAndroid Build Coastguard Worker
38*cda5da8dSAndroid Build Coastguard Worker        def __init__(self, size):
39*cda5da8dSAndroid Build Coastguard Worker            self.size = size
40*cda5da8dSAndroid Build Coastguard Worker            for i in range(100):
41*cda5da8dSAndroid Build Coastguard Worker                name = 'pym-%d-%s' % (os.getpid(), next(self._rand))
42*cda5da8dSAndroid Build Coastguard Worker                buf = mmap.mmap(-1, size, tagname=name)
43*cda5da8dSAndroid Build Coastguard Worker                if _winapi.GetLastError() == 0:
44*cda5da8dSAndroid Build Coastguard Worker                    break
45*cda5da8dSAndroid Build Coastguard Worker                # We have reopened a preexisting mmap.
46*cda5da8dSAndroid Build Coastguard Worker                buf.close()
47*cda5da8dSAndroid Build Coastguard Worker            else:
48*cda5da8dSAndroid Build Coastguard Worker                raise FileExistsError('Cannot find name for new mmap')
49*cda5da8dSAndroid Build Coastguard Worker            self.name = name
50*cda5da8dSAndroid Build Coastguard Worker            self.buffer = buf
51*cda5da8dSAndroid Build Coastguard Worker            self._state = (self.size, self.name)
52*cda5da8dSAndroid Build Coastguard Worker
53*cda5da8dSAndroid Build Coastguard Worker        def __getstate__(self):
54*cda5da8dSAndroid Build Coastguard Worker            assert_spawning(self)
55*cda5da8dSAndroid Build Coastguard Worker            return self._state
56*cda5da8dSAndroid Build Coastguard Worker
57*cda5da8dSAndroid Build Coastguard Worker        def __setstate__(self, state):
58*cda5da8dSAndroid Build Coastguard Worker            self.size, self.name = self._state = state
59*cda5da8dSAndroid Build Coastguard Worker            # Reopen existing mmap
60*cda5da8dSAndroid Build Coastguard Worker            self.buffer = mmap.mmap(-1, self.size, tagname=self.name)
61*cda5da8dSAndroid Build Coastguard Worker            # XXX Temporarily preventing buildbot failures while determining
62*cda5da8dSAndroid Build Coastguard Worker            # XXX the correct long-term fix. See issue 23060
63*cda5da8dSAndroid Build Coastguard Worker            #assert _winapi.GetLastError() == _winapi.ERROR_ALREADY_EXISTS
64*cda5da8dSAndroid Build Coastguard Worker
65*cda5da8dSAndroid Build Coastguard Workerelse:
66*cda5da8dSAndroid Build Coastguard Worker
67*cda5da8dSAndroid Build Coastguard Worker    class Arena(object):
68*cda5da8dSAndroid Build Coastguard Worker        """
69*cda5da8dSAndroid Build Coastguard Worker        A shared memory area backed by a temporary file (POSIX).
70*cda5da8dSAndroid Build Coastguard Worker        """
71*cda5da8dSAndroid Build Coastguard Worker
72*cda5da8dSAndroid Build Coastguard Worker        if sys.platform == 'linux':
73*cda5da8dSAndroid Build Coastguard Worker            _dir_candidates = ['/dev/shm']
74*cda5da8dSAndroid Build Coastguard Worker        else:
75*cda5da8dSAndroid Build Coastguard Worker            _dir_candidates = []
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker        def __init__(self, size, fd=-1):
78*cda5da8dSAndroid Build Coastguard Worker            self.size = size
79*cda5da8dSAndroid Build Coastguard Worker            self.fd = fd
80*cda5da8dSAndroid Build Coastguard Worker            if fd == -1:
81*cda5da8dSAndroid Build Coastguard Worker                # Arena is created anew (if fd != -1, it means we're coming
82*cda5da8dSAndroid Build Coastguard Worker                # from rebuild_arena() below)
83*cda5da8dSAndroid Build Coastguard Worker                self.fd, name = tempfile.mkstemp(
84*cda5da8dSAndroid Build Coastguard Worker                     prefix='pym-%d-'%os.getpid(),
85*cda5da8dSAndroid Build Coastguard Worker                     dir=self._choose_dir(size))
86*cda5da8dSAndroid Build Coastguard Worker                os.unlink(name)
87*cda5da8dSAndroid Build Coastguard Worker                util.Finalize(self, os.close, (self.fd,))
88*cda5da8dSAndroid Build Coastguard Worker                os.ftruncate(self.fd, size)
89*cda5da8dSAndroid Build Coastguard Worker            self.buffer = mmap.mmap(self.fd, self.size)
90*cda5da8dSAndroid Build Coastguard Worker
91*cda5da8dSAndroid Build Coastguard Worker        def _choose_dir(self, size):
92*cda5da8dSAndroid Build Coastguard Worker            # Choose a non-storage backed directory if possible,
93*cda5da8dSAndroid Build Coastguard Worker            # to improve performance
94*cda5da8dSAndroid Build Coastguard Worker            for d in self._dir_candidates:
95*cda5da8dSAndroid Build Coastguard Worker                st = os.statvfs(d)
96*cda5da8dSAndroid Build Coastguard Worker                if st.f_bavail * st.f_frsize >= size:  # enough free space?
97*cda5da8dSAndroid Build Coastguard Worker                    return d
98*cda5da8dSAndroid Build Coastguard Worker            return util.get_temp_dir()
99*cda5da8dSAndroid Build Coastguard Worker
100*cda5da8dSAndroid Build Coastguard Worker    def reduce_arena(a):
101*cda5da8dSAndroid Build Coastguard Worker        if a.fd == -1:
102*cda5da8dSAndroid Build Coastguard Worker            raise ValueError('Arena is unpicklable because '
103*cda5da8dSAndroid Build Coastguard Worker                             'forking was enabled when it was created')
104*cda5da8dSAndroid Build Coastguard Worker        return rebuild_arena, (a.size, reduction.DupFd(a.fd))
105*cda5da8dSAndroid Build Coastguard Worker
106*cda5da8dSAndroid Build Coastguard Worker    def rebuild_arena(size, dupfd):
107*cda5da8dSAndroid Build Coastguard Worker        return Arena(size, dupfd.detach())
108*cda5da8dSAndroid Build Coastguard Worker
109*cda5da8dSAndroid Build Coastguard Worker    reduction.register(Arena, reduce_arena)
110*cda5da8dSAndroid Build Coastguard Worker
111*cda5da8dSAndroid Build Coastguard Worker#
112*cda5da8dSAndroid Build Coastguard Worker# Class allowing allocation of chunks of memory from arenas
113*cda5da8dSAndroid Build Coastguard Worker#
114*cda5da8dSAndroid Build Coastguard Worker
115*cda5da8dSAndroid Build Coastguard Workerclass Heap(object):
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard Worker    # Minimum malloc() alignment
118*cda5da8dSAndroid Build Coastguard Worker    _alignment = 8
119*cda5da8dSAndroid Build Coastguard Worker
120*cda5da8dSAndroid Build Coastguard Worker    _DISCARD_FREE_SPACE_LARGER_THAN = 4 * 1024 ** 2  # 4 MB
121*cda5da8dSAndroid Build Coastguard Worker    _DOUBLE_ARENA_SIZE_UNTIL = 4 * 1024 ** 2
122*cda5da8dSAndroid Build Coastguard Worker
123*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, size=mmap.PAGESIZE):
124*cda5da8dSAndroid Build Coastguard Worker        self._lastpid = os.getpid()
125*cda5da8dSAndroid Build Coastguard Worker        self._lock = threading.Lock()
126*cda5da8dSAndroid Build Coastguard Worker        # Current arena allocation size
127*cda5da8dSAndroid Build Coastguard Worker        self._size = size
128*cda5da8dSAndroid Build Coastguard Worker        # A sorted list of available block sizes in arenas
129*cda5da8dSAndroid Build Coastguard Worker        self._lengths = []
130*cda5da8dSAndroid Build Coastguard Worker
131*cda5da8dSAndroid Build Coastguard Worker        # Free block management:
132*cda5da8dSAndroid Build Coastguard Worker        # - map each block size to a list of `(Arena, start, stop)` blocks
133*cda5da8dSAndroid Build Coastguard Worker        self._len_to_seq = {}
134*cda5da8dSAndroid Build Coastguard Worker        # - map `(Arena, start)` tuple to the `(Arena, start, stop)` block
135*cda5da8dSAndroid Build Coastguard Worker        #   starting at that offset
136*cda5da8dSAndroid Build Coastguard Worker        self._start_to_block = {}
137*cda5da8dSAndroid Build Coastguard Worker        # - map `(Arena, stop)` tuple to the `(Arena, start, stop)` block
138*cda5da8dSAndroid Build Coastguard Worker        #   ending at that offset
139*cda5da8dSAndroid Build Coastguard Worker        self._stop_to_block = {}
140*cda5da8dSAndroid Build Coastguard Worker
141*cda5da8dSAndroid Build Coastguard Worker        # Map arenas to their `(Arena, start, stop)` blocks in use
142*cda5da8dSAndroid Build Coastguard Worker        self._allocated_blocks = defaultdict(set)
143*cda5da8dSAndroid Build Coastguard Worker        self._arenas = []
144*cda5da8dSAndroid Build Coastguard Worker
145*cda5da8dSAndroid Build Coastguard Worker        # List of pending blocks to free - see comment in free() below
146*cda5da8dSAndroid Build Coastguard Worker        self._pending_free_blocks = []
147*cda5da8dSAndroid Build Coastguard Worker
148*cda5da8dSAndroid Build Coastguard Worker        # Statistics
149*cda5da8dSAndroid Build Coastguard Worker        self._n_mallocs = 0
150*cda5da8dSAndroid Build Coastguard Worker        self._n_frees = 0
151*cda5da8dSAndroid Build Coastguard Worker
152*cda5da8dSAndroid Build Coastguard Worker    @staticmethod
153*cda5da8dSAndroid Build Coastguard Worker    def _roundup(n, alignment):
154*cda5da8dSAndroid Build Coastguard Worker        # alignment must be a power of 2
155*cda5da8dSAndroid Build Coastguard Worker        mask = alignment - 1
156*cda5da8dSAndroid Build Coastguard Worker        return (n + mask) & ~mask
157*cda5da8dSAndroid Build Coastguard Worker
158*cda5da8dSAndroid Build Coastguard Worker    def _new_arena(self, size):
159*cda5da8dSAndroid Build Coastguard Worker        # Create a new arena with at least the given *size*
160*cda5da8dSAndroid Build Coastguard Worker        length = self._roundup(max(self._size, size), mmap.PAGESIZE)
161*cda5da8dSAndroid Build Coastguard Worker        # We carve larger and larger arenas, for efficiency, until we
162*cda5da8dSAndroid Build Coastguard Worker        # reach a large-ish size (roughly L3 cache-sized)
163*cda5da8dSAndroid Build Coastguard Worker        if self._size < self._DOUBLE_ARENA_SIZE_UNTIL:
164*cda5da8dSAndroid Build Coastguard Worker            self._size *= 2
165*cda5da8dSAndroid Build Coastguard Worker        util.info('allocating a new mmap of length %d', length)
166*cda5da8dSAndroid Build Coastguard Worker        arena = Arena(length)
167*cda5da8dSAndroid Build Coastguard Worker        self._arenas.append(arena)
168*cda5da8dSAndroid Build Coastguard Worker        return (arena, 0, length)
169*cda5da8dSAndroid Build Coastguard Worker
170*cda5da8dSAndroid Build Coastguard Worker    def _discard_arena(self, arena):
171*cda5da8dSAndroid Build Coastguard Worker        # Possibly delete the given (unused) arena
172*cda5da8dSAndroid Build Coastguard Worker        length = arena.size
173*cda5da8dSAndroid Build Coastguard Worker        # Reusing an existing arena is faster than creating a new one, so
174*cda5da8dSAndroid Build Coastguard Worker        # we only reclaim space if it's large enough.
175*cda5da8dSAndroid Build Coastguard Worker        if length < self._DISCARD_FREE_SPACE_LARGER_THAN:
176*cda5da8dSAndroid Build Coastguard Worker            return
177*cda5da8dSAndroid Build Coastguard Worker        blocks = self._allocated_blocks.pop(arena)
178*cda5da8dSAndroid Build Coastguard Worker        assert not blocks
179*cda5da8dSAndroid Build Coastguard Worker        del self._start_to_block[(arena, 0)]
180*cda5da8dSAndroid Build Coastguard Worker        del self._stop_to_block[(arena, length)]
181*cda5da8dSAndroid Build Coastguard Worker        self._arenas.remove(arena)
182*cda5da8dSAndroid Build Coastguard Worker        seq = self._len_to_seq[length]
183*cda5da8dSAndroid Build Coastguard Worker        seq.remove((arena, 0, length))
184*cda5da8dSAndroid Build Coastguard Worker        if not seq:
185*cda5da8dSAndroid Build Coastguard Worker            del self._len_to_seq[length]
186*cda5da8dSAndroid Build Coastguard Worker            self._lengths.remove(length)
187*cda5da8dSAndroid Build Coastguard Worker
188*cda5da8dSAndroid Build Coastguard Worker    def _malloc(self, size):
189*cda5da8dSAndroid Build Coastguard Worker        # returns a large enough block -- it might be much larger
190*cda5da8dSAndroid Build Coastguard Worker        i = bisect.bisect_left(self._lengths, size)
191*cda5da8dSAndroid Build Coastguard Worker        if i == len(self._lengths):
192*cda5da8dSAndroid Build Coastguard Worker            return self._new_arena(size)
193*cda5da8dSAndroid Build Coastguard Worker        else:
194*cda5da8dSAndroid Build Coastguard Worker            length = self._lengths[i]
195*cda5da8dSAndroid Build Coastguard Worker            seq = self._len_to_seq[length]
196*cda5da8dSAndroid Build Coastguard Worker            block = seq.pop()
197*cda5da8dSAndroid Build Coastguard Worker            if not seq:
198*cda5da8dSAndroid Build Coastguard Worker                del self._len_to_seq[length], self._lengths[i]
199*cda5da8dSAndroid Build Coastguard Worker
200*cda5da8dSAndroid Build Coastguard Worker        (arena, start, stop) = block
201*cda5da8dSAndroid Build Coastguard Worker        del self._start_to_block[(arena, start)]
202*cda5da8dSAndroid Build Coastguard Worker        del self._stop_to_block[(arena, stop)]
203*cda5da8dSAndroid Build Coastguard Worker        return block
204*cda5da8dSAndroid Build Coastguard Worker
205*cda5da8dSAndroid Build Coastguard Worker    def _add_free_block(self, block):
206*cda5da8dSAndroid Build Coastguard Worker        # make block available and try to merge with its neighbours in the arena
207*cda5da8dSAndroid Build Coastguard Worker        (arena, start, stop) = block
208*cda5da8dSAndroid Build Coastguard Worker
209*cda5da8dSAndroid Build Coastguard Worker        try:
210*cda5da8dSAndroid Build Coastguard Worker            prev_block = self._stop_to_block[(arena, start)]
211*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
212*cda5da8dSAndroid Build Coastguard Worker            pass
213*cda5da8dSAndroid Build Coastguard Worker        else:
214*cda5da8dSAndroid Build Coastguard Worker            start, _ = self._absorb(prev_block)
215*cda5da8dSAndroid Build Coastguard Worker
216*cda5da8dSAndroid Build Coastguard Worker        try:
217*cda5da8dSAndroid Build Coastguard Worker            next_block = self._start_to_block[(arena, stop)]
218*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
219*cda5da8dSAndroid Build Coastguard Worker            pass
220*cda5da8dSAndroid Build Coastguard Worker        else:
221*cda5da8dSAndroid Build Coastguard Worker            _, stop = self._absorb(next_block)
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker        block = (arena, start, stop)
224*cda5da8dSAndroid Build Coastguard Worker        length = stop - start
225*cda5da8dSAndroid Build Coastguard Worker
226*cda5da8dSAndroid Build Coastguard Worker        try:
227*cda5da8dSAndroid Build Coastguard Worker            self._len_to_seq[length].append(block)
228*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
229*cda5da8dSAndroid Build Coastguard Worker            self._len_to_seq[length] = [block]
230*cda5da8dSAndroid Build Coastguard Worker            bisect.insort(self._lengths, length)
231*cda5da8dSAndroid Build Coastguard Worker
232*cda5da8dSAndroid Build Coastguard Worker        self._start_to_block[(arena, start)] = block
233*cda5da8dSAndroid Build Coastguard Worker        self._stop_to_block[(arena, stop)] = block
234*cda5da8dSAndroid Build Coastguard Worker
235*cda5da8dSAndroid Build Coastguard Worker    def _absorb(self, block):
236*cda5da8dSAndroid Build Coastguard Worker        # deregister this block so it can be merged with a neighbour
237*cda5da8dSAndroid Build Coastguard Worker        (arena, start, stop) = block
238*cda5da8dSAndroid Build Coastguard Worker        del self._start_to_block[(arena, start)]
239*cda5da8dSAndroid Build Coastguard Worker        del self._stop_to_block[(arena, stop)]
240*cda5da8dSAndroid Build Coastguard Worker
241*cda5da8dSAndroid Build Coastguard Worker        length = stop - start
242*cda5da8dSAndroid Build Coastguard Worker        seq = self._len_to_seq[length]
243*cda5da8dSAndroid Build Coastguard Worker        seq.remove(block)
244*cda5da8dSAndroid Build Coastguard Worker        if not seq:
245*cda5da8dSAndroid Build Coastguard Worker            del self._len_to_seq[length]
246*cda5da8dSAndroid Build Coastguard Worker            self._lengths.remove(length)
247*cda5da8dSAndroid Build Coastguard Worker
248*cda5da8dSAndroid Build Coastguard Worker        return start, stop
249*cda5da8dSAndroid Build Coastguard Worker
250*cda5da8dSAndroid Build Coastguard Worker    def _remove_allocated_block(self, block):
251*cda5da8dSAndroid Build Coastguard Worker        arena, start, stop = block
252*cda5da8dSAndroid Build Coastguard Worker        blocks = self._allocated_blocks[arena]
253*cda5da8dSAndroid Build Coastguard Worker        blocks.remove((start, stop))
254*cda5da8dSAndroid Build Coastguard Worker        if not blocks:
255*cda5da8dSAndroid Build Coastguard Worker            # Arena is entirely free, discard it from this process
256*cda5da8dSAndroid Build Coastguard Worker            self._discard_arena(arena)
257*cda5da8dSAndroid Build Coastguard Worker
258*cda5da8dSAndroid Build Coastguard Worker    def _free_pending_blocks(self):
259*cda5da8dSAndroid Build Coastguard Worker        # Free all the blocks in the pending list - called with the lock held.
260*cda5da8dSAndroid Build Coastguard Worker        while True:
261*cda5da8dSAndroid Build Coastguard Worker            try:
262*cda5da8dSAndroid Build Coastguard Worker                block = self._pending_free_blocks.pop()
263*cda5da8dSAndroid Build Coastguard Worker            except IndexError:
264*cda5da8dSAndroid Build Coastguard Worker                break
265*cda5da8dSAndroid Build Coastguard Worker            self._add_free_block(block)
266*cda5da8dSAndroid Build Coastguard Worker            self._remove_allocated_block(block)
267*cda5da8dSAndroid Build Coastguard Worker
268*cda5da8dSAndroid Build Coastguard Worker    def free(self, block):
269*cda5da8dSAndroid Build Coastguard Worker        # free a block returned by malloc()
270*cda5da8dSAndroid Build Coastguard Worker        # Since free() can be called asynchronously by the GC, it could happen
271*cda5da8dSAndroid Build Coastguard Worker        # that it's called while self._lock is held: in that case,
272*cda5da8dSAndroid Build Coastguard Worker        # self._lock.acquire() would deadlock (issue #12352). To avoid that, a
273*cda5da8dSAndroid Build Coastguard Worker        # trylock is used instead, and if the lock can't be acquired
274*cda5da8dSAndroid Build Coastguard Worker        # immediately, the block is added to a list of blocks to be freed
275*cda5da8dSAndroid Build Coastguard Worker        # synchronously sometimes later from malloc() or free(), by calling
276*cda5da8dSAndroid Build Coastguard Worker        # _free_pending_blocks() (appending and retrieving from a list is not
277*cda5da8dSAndroid Build Coastguard Worker        # strictly thread-safe but under CPython it's atomic thanks to the GIL).
278*cda5da8dSAndroid Build Coastguard Worker        if os.getpid() != self._lastpid:
279*cda5da8dSAndroid Build Coastguard Worker            raise ValueError(
280*cda5da8dSAndroid Build Coastguard Worker                "My pid ({0:n}) is not last pid {1:n}".format(
281*cda5da8dSAndroid Build Coastguard Worker                    os.getpid(),self._lastpid))
282*cda5da8dSAndroid Build Coastguard Worker        if not self._lock.acquire(False):
283*cda5da8dSAndroid Build Coastguard Worker            # can't acquire the lock right now, add the block to the list of
284*cda5da8dSAndroid Build Coastguard Worker            # pending blocks to free
285*cda5da8dSAndroid Build Coastguard Worker            self._pending_free_blocks.append(block)
286*cda5da8dSAndroid Build Coastguard Worker        else:
287*cda5da8dSAndroid Build Coastguard Worker            # we hold the lock
288*cda5da8dSAndroid Build Coastguard Worker            try:
289*cda5da8dSAndroid Build Coastguard Worker                self._n_frees += 1
290*cda5da8dSAndroid Build Coastguard Worker                self._free_pending_blocks()
291*cda5da8dSAndroid Build Coastguard Worker                self._add_free_block(block)
292*cda5da8dSAndroid Build Coastguard Worker                self._remove_allocated_block(block)
293*cda5da8dSAndroid Build Coastguard Worker            finally:
294*cda5da8dSAndroid Build Coastguard Worker                self._lock.release()
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Worker    def malloc(self, size):
297*cda5da8dSAndroid Build Coastguard Worker        # return a block of right size (possibly rounded up)
298*cda5da8dSAndroid Build Coastguard Worker        if size < 0:
299*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Size {0:n} out of range".format(size))
300*cda5da8dSAndroid Build Coastguard Worker        if sys.maxsize <= size:
301*cda5da8dSAndroid Build Coastguard Worker            raise OverflowError("Size {0:n} too large".format(size))
302*cda5da8dSAndroid Build Coastguard Worker        if os.getpid() != self._lastpid:
303*cda5da8dSAndroid Build Coastguard Worker            self.__init__()                     # reinitialize after fork
304*cda5da8dSAndroid Build Coastguard Worker        with self._lock:
305*cda5da8dSAndroid Build Coastguard Worker            self._n_mallocs += 1
306*cda5da8dSAndroid Build Coastguard Worker            # allow pending blocks to be marked available
307*cda5da8dSAndroid Build Coastguard Worker            self._free_pending_blocks()
308*cda5da8dSAndroid Build Coastguard Worker            size = self._roundup(max(size, 1), self._alignment)
309*cda5da8dSAndroid Build Coastguard Worker            (arena, start, stop) = self._malloc(size)
310*cda5da8dSAndroid Build Coastguard Worker            real_stop = start + size
311*cda5da8dSAndroid Build Coastguard Worker            if real_stop < stop:
312*cda5da8dSAndroid Build Coastguard Worker                # if the returned block is larger than necessary, mark
313*cda5da8dSAndroid Build Coastguard Worker                # the remainder available
314*cda5da8dSAndroid Build Coastguard Worker                self._add_free_block((arena, real_stop, stop))
315*cda5da8dSAndroid Build Coastguard Worker            self._allocated_blocks[arena].add((start, real_stop))
316*cda5da8dSAndroid Build Coastguard Worker            return (arena, start, real_stop)
317*cda5da8dSAndroid Build Coastguard Worker
318*cda5da8dSAndroid Build Coastguard Worker#
319*cda5da8dSAndroid Build Coastguard Worker# Class wrapping a block allocated out of a Heap -- can be inherited by child process
320*cda5da8dSAndroid Build Coastguard Worker#
321*cda5da8dSAndroid Build Coastguard Worker
322*cda5da8dSAndroid Build Coastguard Workerclass BufferWrapper(object):
323*cda5da8dSAndroid Build Coastguard Worker
324*cda5da8dSAndroid Build Coastguard Worker    _heap = Heap()
325*cda5da8dSAndroid Build Coastguard Worker
326*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, size):
327*cda5da8dSAndroid Build Coastguard Worker        if size < 0:
328*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("Size {0:n} out of range".format(size))
329*cda5da8dSAndroid Build Coastguard Worker        if sys.maxsize <= size:
330*cda5da8dSAndroid Build Coastguard Worker            raise OverflowError("Size {0:n} too large".format(size))
331*cda5da8dSAndroid Build Coastguard Worker        block = BufferWrapper._heap.malloc(size)
332*cda5da8dSAndroid Build Coastguard Worker        self._state = (block, size)
333*cda5da8dSAndroid Build Coastguard Worker        util.Finalize(self, BufferWrapper._heap.free, args=(block,))
334*cda5da8dSAndroid Build Coastguard Worker
335*cda5da8dSAndroid Build Coastguard Worker    def create_memoryview(self):
336*cda5da8dSAndroid Build Coastguard Worker        (arena, start, stop), size = self._state
337*cda5da8dSAndroid Build Coastguard Worker        return memoryview(arena.buffer)[start:start+size]
338