1010from weakref import ref
1111import sys
1212
13- __all__ = ["SlidingWindowMapManager" ]
13+ __all__ = ["StaticWindowMapManager" , " SlidingWindowMapManager" ]
1414#{ Utilities
1515
1616#}END utilities
@@ -125,11 +125,11 @@ def unuse_region(self):
125125
126126 def buffer (self ):
127127 """Return a buffer object which allows access to our memory region from our offset
128- to the window size. Please note that it might be smaller than you requested
128+ to the window size. Please note that it might be smaller than you requested when calling use_region()
129129 :note: You can only obtain a buffer if this instance is_valid() !
130130 :note: buffers should not be cached passed the duration of your access as it will
131131 prevent resources from being freed even though they might not be accounted for anymore !"""
132- return buffer (self ._region .buffer (), self ._ofs , self ._size )
132+ return buffer (self ._region .map (), self ._ofs , self ._size )
133133
134134 def is_valid (self ):
135135 """:return: True if we have a valid and usable region"""
@@ -195,19 +195,17 @@ def fd(self):
195195 #} END interface
196196
197197
198+ class StaticWindowMapManager (object ):
199+ """Provides a manager which will produce single size cursors that are allowed
200+ to always map the whole file.
198201
199- class SlidingWindowMapManager (object ):
200- """Maintains a list of ranges of mapped memory regions in one or more files and allows to easily
201- obtain additional regions assuring there is no overlap.
202- Once a certain memory limit is reached globally, or if there cannot be more open file handles
203- which result from each mmap call, the least recently used, and currently unused mapped regions
204- are unloaded automatically.
202+ Clients must be written to specifically know that they are accessing their data
203+ through a StaticWindowMapManager, as they otherwise have to deal with their window size.
205204
206- :note: currently not thread-safe !
207- :note: in the current implementation, we will automatically unload windows if we either cannot
208- create more memory maps (as the open file handles limit is hit) or if we have allocated more than
209- a safe amount of memory already, which would possibly cause memory allocations to fail as our address
210- space is full."""
205+ These clients would have to use a SlidingWindowMapBuffer to hide this fact.
206+
207+ This type will always use a maximum window size, and optimize certain methods to
208+ acomodate this fact"""
211209
212210 __slots__ = [
213211 '_fdict' , # mapping of path -> MapRegionList
@@ -222,11 +220,12 @@ class SlidingWindowMapManager(object):
222220 MapRegionListCls = MapRegionList
223221 MapWindowCls = MapWindow
224222 MapRegionCls = MapRegion
223+ MemoryCursorCls = MemoryCursor
225224 #} END configuration
226225
227226 _MB_in_bytes = 1024 * 1024
228227
229- def __init__ (self , window_size = 0 , max_memory_size = 0 , max_open_handles = sys .maxint ):
228+ def __init__ (self , window_size = sys . maxint , max_memory_size = 0 , max_open_handles = sys .maxint ):
230229 """initialize the manager with the given parameters.
231230 :param window_size: if 0, a default window size will be chosen depending on
232231 the operating system's architechture. It will internally be quantified to a multiple of the page size
@@ -258,13 +257,126 @@ def __init__(self, window_size = 0, max_memory_size = 0, max_open_handles = sys.
258257 self ._max_memory_size = coeff * self ._MB_in_bytes
259258 #END handle max memory size
260259
260+ #{ Internal Methods
261+
261262 def _collect_lru_region (self , size ):
262263 """Unmap the region which was least-recently used and has no client
263264 :param size: size of the region we want to map next (assuming its not already mapped partially or full
264265 if 0, we try to free any available region
265266 :raise RegionCollectionError:
266267 :return: Amount of freed regions
267268 :todo: implement a case where all unusued regions are discarded efficiently. Currently its only brute force"""
269+ raise NotImplementedError ()
270+
271+ def _obtain_region (self , a , offset , size , flags , is_recursive ):
272+ """Utilty to create a new region - for more information on the parameters,
273+ see MapCursor.use_region.
274+ :param a: A regions (a)rray
275+ :return: The newly created region"""
276+ raise NotImplementedError ()
277+
278+ #}END internal methods
279+
280+ #{ Interface
281+ def make_cursor (self , path_or_fd ):
282+ """:return: a cursor pointing to the given path or file descriptor.
283+ It can be used to map new regions of the file into memory
284+ :note: if a file descriptor is given, it is assumed to be open and valid,
285+ but may be closed afterwards. To refer to the same file, you may reuse
286+ your existing file descriptor, but keep in mind that new windows can only
287+ be mapped as long as it stays valid. This is why the using actual file paths
288+ are preferred unless you plan to keep the file descriptor open.
289+ :note: Using file descriptors directly is faster once new windows are mapped as it
290+ prevents the file to be opened again just for the purpose of mapping it."""
291+ regions = self ._fdict .get (path_or_fd )
292+ if regions is None :
293+ regions = self .MapRegionListCls (path_or_fd )
294+ self ._fdict [path_or_fd ] = regions
295+ # END obtain region for path
296+ return self .MemoryCursorCls (self , regions )
297+
298+ def collect (self ):
299+ """Collect all available free-to-collect mapped regions
300+ :return: Amount of freed handles"""
301+ return self ._collect_lru_region (0 )
302+
303+ def num_file_handles (self ):
304+ """:return: amount of file handles in use. Each mapped region uses one file handle"""
305+ return self ._handle_count
306+
307+ def num_open_files (self ):
308+ """Amount of opened files in the system"""
309+ return reduce (lambda x ,y : x + y , (1 for rlist in self ._fdict .itervalues () if len (rlist ) > 0 ), 0 )
310+
311+ def window_size (self ):
312+ """:return: size of each window when allocating new regions"""
313+ return self ._window_size
314+
315+ def mapped_memory_size (self ):
316+ """:return: amount of bytes currently mapped in total"""
317+ return self ._memory_size
318+
319+ def max_file_handles (self ):
320+ """:return: maximium amount of handles we may have opened"""
321+ return self ._max_handle_count
322+
323+ def max_mapped_memory_size (self ):
324+ """:return: maximum amount of memory we may allocate"""
325+ return self ._max_memory_size
326+
327+ #} END interface
328+
329+ #{ Special Purpose Interface
330+
331+ def force_map_handle_removal_win (self , base_path ):
332+ """ONLY AVAILABLE ON WINDOWS
333+ On windows removing files is not allowed if anybody still has it opened.
334+ If this process is ourselves, and if the whole process uses this memory
335+ manager (as far as the parent framework is concerned) we can enforce
336+ closing all memory maps whose path matches the given base path to
337+ allow the respective operation after all.
338+ The respective system must NOT access the closed memory regions anymore !
339+ This really may only be used if you know that the items which keep
340+ the cursors alive will not be using it anymore. They need to be recreated !
341+ :return: Amount of closed handles
342+ :note: does nothing on non-windows platforms"""
343+ if sys .platform != 'win32' :
344+ return
345+ #END early bailout
346+
347+ num_closed = 0
348+ for path , rlist in self ._fdict .iteritems ():
349+ if path .startswith (base_path ):
350+ for region in rlist :
351+ region ._mf .close ()
352+ num_closed += 1
353+ #END path matches
354+ #END for each path
355+ return num_closed
356+ #} END special purpose interface
357+
358+
359+
360+ class SlidingWindowMapManager (StaticWindowMapManager ):
361+ """Maintains a list of ranges of mapped memory regions in one or more files and allows to easily
362+ obtain additional regions assuring there is no overlap.
363+ Once a certain memory limit is reached globally, or if there cannot be more open file handles
364+ which result from each mmap call, the least recently used, and currently unused mapped regions
365+ are unloaded automatically.
366+
367+ :note: currently not thread-safe !
368+ :note: in the current implementation, we will automatically unload windows if we either cannot
369+ create more memory maps (as the open file handles limit is hit) or if we have allocated more than
370+ a safe amount of memory already, which would possibly cause memory allocations to fail as our address
371+ space is full."""
372+
373+ __slots__ = tuple ()
374+
375+ def __init__ (self , window_size = 0 , max_memory_size = 0 , max_open_handles = sys .maxint ):
376+ """Adjusts the default window size to 0"""
377+ super (SlidingWindowMapManager , self ).__init__ (window_size , max_memory_size , max_open_handles )
378+
379+ def _collect_lru_region (self , size ):
268380 num_found = 0
269381 while (size == 0 ) or (self ._memory_size + size > self ._max_memory_size ):
270382 lru_region = None
@@ -296,10 +408,6 @@ def _collect_lru_region(self, size):
296408 return num_found
297409
298410 def _obtain_region (self , a , offset , size , flags , is_recursive ):
299- """Utilty to create a new region - for more information on the parameters,
300- see MapCursor.use_region.
301- :param a: A regions (a)rray
302- :return: The newly created region"""
303411 # bisect to find an existing region. The c++ implementation cannot
304412 # do that as it uses a linked list for regions.
305413 r = None
@@ -400,80 +508,4 @@ def _obtain_region(self, a, offset, size, flags, is_recursive):
400508 # END create new region
401509 return r
402510
403- #{ Interface
404- def make_cursor (self , path_or_fd ):
405- """:return: a cursor pointing to the given path or file descriptor.
406- It can be used to map new regions of the file into memory
407- :note: if a file descriptor is given, it is assumed to be open and valid,
408- but may be closed afterwards. To refer to the same file, you may reuse
409- your existing file descriptor, but keep in mind that new windows can only
410- be mapped as long as it stays valid. This is why the using actual file paths
411- are preferred unless you plan to keep the file descriptor open.
412- :note: Using file descriptors directly is faster once new windows are mapped as it
413- prevents the file to be opened again just for the purpose of mapping it."""
414- regions = self ._fdict .get (path_or_fd )
415- if regions is None :
416- regions = self .MapRegionListCls (path_or_fd )
417- self ._fdict [path_or_fd ] = regions
418- # END obtain region for path
419- return MemoryCursor (self , regions )
420-
421- def collect (self ):
422- """Collect all available free-to-collect mapped regions
423- :return: Amount of freed handles"""
424- return self ._collect_lru_region (0 )
425-
426- def num_file_handles (self ):
427- """:return: amount of file handles in use. Each mapped region uses one file handle"""
428- return self ._handle_count
429511
430- def num_open_files (self ):
431- """Amount of opened files in the system"""
432- return reduce (lambda x ,y : x + y , (1 for rlist in self ._fdict .itervalues () if len (rlist ) > 0 ), 0 )
433-
434- def window_size (self ):
435- """:return: size of each window when allocating new regions"""
436- return self ._window_size
437-
438- def mapped_memory_size (self ):
439- """:return: amount of bytes currently mapped in total"""
440- return self ._memory_size
441-
442- def max_file_handles (self ):
443- """:return: maximium amount of handles we may have opened"""
444- return self ._max_handle_count
445-
446- def max_mapped_memory_size (self ):
447- """:return: maximum amount of memory we may allocate"""
448- return self ._max_memory_size
449-
450- #} END interface
451-
452- #{ Special Purpose Interface
453-
454- def force_map_handle_removal_win (self , base_path ):
455- """ONLY AVAILABLE ON WINDOWS
456- On windows removing files is not allowed if anybody still has it opened.
457- If this process is ourselves, and if the whole process uses this memory
458- manager (as far as the parent framework is concerned) we can enforce
459- closing all memory maps whose path matches the given base path to
460- allow the respective operation after all.
461- The respective system must NOT access the closed memory regions anymore !
462- This really may only be used if you know that the items which keep
463- the cursors alive will not be using it anymore. They need to be recreated !
464- :return: Amount of closed handles
465- :note: does nothing on non-windows platforms"""
466- if sys .platform != 'win32' :
467- return
468- #END early bailout
469-
470- num_closed = 0
471- for path , rlist in self ._fdict .iteritems ():
472- if path .startswith (base_path ):
473- for region in rlist :
474- region ._mf .close ()
475- num_closed += 1
476- #END path matches
477- #END for each path
478- return num_closed
479- #} END special purpose interface
0 commit comments