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