99from exc import RegionCollectionError
1010from weakref import ref
1111import sys
12+ from sys import getrefcount
1213
1314__all__ = ["StaticWindowMapManager" , "SlidingWindowMapManager" ]
1415#{ Utilities
1516
1617#}END utilities
1718
18- class MemoryCursor (object ):
19- """Pointer into the mapped region of the memory manager, keeping the current window
20- alive until it is destroyed.
19+
20+
21+ class WindowCursor (object ):
22+ """Pointer into the mapped region of the memory manager, keeping the map
23+ alive until it is destroyed and no other client uses it.
2124
22- Cursors should not be created manually, but are instead returned by the SlidingWindowMapManager"""
25+ Cursors should not be created manually, but are instead returned by the SlidingWindowMapManager
26+ :note: The current implementation is suited for static and sliding window managers, but it also means
27+ that it must be suited for the somewhat quite different sliding manager. It could be improved, but
28+ I see no real need to do so."""
2329 __slots__ = (
2430 '_manager' , # the manger keeping all file regions
2531 '_rlist' , # a regions list with regions for our file
@@ -130,7 +136,14 @@ def buffer(self):
130136 :note: buffers should not be cached passed the duration of your access as it will
131137 prevent resources from being freed even though they might not be accounted for anymore !"""
132138 return buffer (self ._region .map (), self ._ofs , self ._size )
133-
139+
140+ def map (self ):
141+ """
142+ :return: the underlying raw memory map. Please not that the offset and size is likely to be different
143+ to what you set as offset and size. Use it only if you are sure about the region it maps, which is the whole
144+ file in case of StaticWindowMapManager"""
145+ return self ._region .map ()
146+
134147 def is_valid (self ):
135148 """:return: True if we have a valid and usable region"""
136149 return self ._region is not None
@@ -188,7 +201,7 @@ def fd(self):
188201 :note: it is not required to be valid anymore
189202 :raise ValueError: if the mapping was not created by a file descriptor"""
190203 if isinstance (self ._rlist .path_or_fd (), basestring ):
191- return ValueError ("File descriptor queried although mapping was generated from path" )
204+ raise ValueError ("File descriptor queried although mapping was generated from path" )
192205 #END handle type
193206 return self ._rlist .path_or_fd ()
194207
@@ -208,7 +221,7 @@ class StaticWindowMapManager(object):
208221 acomodate this fact"""
209222
210223 __slots__ = [
211- '_fdict' , # mapping of path -> MapRegionList
224+ '_fdict' , # mapping of path -> StorageHelper (of some kind
212225 '_window_size' , # maximum size of a window
213226 '_max_memory_size' , # maximum amount ofmemory we may allocate
214227 '_max_handle_count' , # maximum amount of handles to keep open
@@ -220,7 +233,7 @@ class StaticWindowMapManager(object):
220233 MapRegionListCls = MapRegionList
221234 MapWindowCls = MapWindow
222235 MapRegionCls = MapRegion
223- MemoryCursorCls = MemoryCursor
236+ WindowCursorCls = WindowCursor
224237 #} END configuration
225238
226239 _MB_in_bytes = 1024 * 1024
@@ -266,14 +279,77 @@ def _collect_lru_region(self, size):
266279 :raise RegionCollectionError:
267280 :return: Amount of freed regions
268281 :todo: implement a case where all unusued regions are discarded efficiently. Currently its only brute force"""
269- raise NotImplementedError ()
282+ num_found = 0
283+ while (size == 0 ) or (self ._memory_size + size > self ._max_memory_size ):
284+ for k , regions in self ._fdict .iteritems ():
285+ found_lonely_region = False
286+ for region in regions :
287+ # check client count - consider that we keep one reference ourselves !
288+ if (region .client_count ()- 2 == 0 and
289+ (lru_region is None or region ._uc < lru_region ._uc )):
290+ # remove whole list
291+ found_lonely_region = True
292+ num_found += 1
293+ self ._memory_size -= region .size ()
294+ self ._handle_count -= 1
295+ self ._fdict .pop (k )
296+
297+ break
298+ # END update lru_region
299+ #END for each region
300+ if found_lonely_region :
301+ continue
302+ # END skip iteration and restart
303+ #END for each regions list
304+
305+ # still here ?
306+ if num_found == 0 and size != 0 :
307+ raise RegionCollectionError ("Didn't find any region to free" )
308+ #END raise if necessary
309+ #END while there is more memory to free
310+
311+ return num_found
312+
270313
271314 def _obtain_region (self , a , offset , size , flags , is_recursive ):
272315 """Utilty to create a new region - for more information on the parameters,
273316 see MapCursor.use_region.
274317 :param a: A regions (a)rray
275318 :return: The newly created region"""
276- raise NotImplementedError ()
319+ if self ._memory_size + window_size > self ._max_memory_size :
320+ self ._collect_lru_region (window_size )
321+ #END handle collection
322+
323+ r = None
324+ if a :
325+ assert len (a ) == 1
326+ r = a [0 ]
327+ else :
328+ try :
329+ r = self .MapRegionCls (a .path_or_fd (), 0 , sys .maxint , flags )
330+ except Exception :
331+ # apparently we are out of system resources or hit a limit
332+ # As many more operations are likely to fail in that condition (
333+ # like reading a file from disk, etc) we free up as much as possible
334+ # As this invalidates our insert position, we have to recurse here
335+ # NOTE: The c++ version uses a linked list to curcumvent this, but
336+ # using that in python is probably too slow anyway
337+ if is_recursive :
338+ # we already tried this, and still have no success in obtaining
339+ # a mapping. This is an exception, so we propagate it
340+ raise
341+ #END handle existing recursion
342+ self ._collect_lru_region (0 )
343+ return self ._obtain_region (a , offset , size , flags , True )
344+ #END handle exceptions
345+
346+ self ._handle_count += 1
347+ self ._memory_size += r .size ()
348+ # END handle array
349+
350+ assert a .includes_ofs (offset )
351+ assert a .includes_ofs (offset + size - 1 )
352+ return r
277353
278354 #}END internal methods
279355
@@ -293,7 +369,7 @@ def make_cursor(self, path_or_fd):
293369 regions = self .MapRegionListCls (path_or_fd )
294370 self ._fdict [path_or_fd ] = regions
295371 # END obtain region for path
296- return self .MemoryCursorCls (self , regions )
372+ return self .WindowCursorCls (self , regions )
297373
298374 def collect (self ):
299375 """Collect all available free-to-collect mapped regions
0 commit comments