Merge branch 'master' into 14885-ciso-and-conda-packaging-pr
[arvados.git] / services / fuse / arvados_fuse / fresh.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 import time
6 import ciso8601
7 import calendar
8 import functools
9
10 def convertTime(t):
11     """Parse Arvados timestamp to unix time."""
12     if not t:
13         return 0
14     try:
15         return calendar.timegm(ciso8601.parse_datetime_as_naive(t).timetuple())
16     except (TypeError, ValueError):
17         return 0
18
19 def use_counter(orig_func):
20     @functools.wraps(orig_func)
21     def use_counter_wrapper(self, *args, **kwargs):
22         try:
23             self.inc_use()
24             return orig_func(self, *args, **kwargs)
25         finally:
26             self.dec_use()
27     return use_counter_wrapper
28
29 def check_update(orig_func):
30     @functools.wraps(orig_func)
31     def check_update_wrapper(self, *args, **kwargs):
32         self.checkupdate()
33         return orig_func(self, *args, **kwargs)
34     return check_update_wrapper
35
36 class FreshBase(object):
37     """Base class for maintaining object lifecycle.
38
39     Functions include:
40
41     * Indicate if an object is up to date (stale() == false) or needs to be
42       updated sets stale() == True).  Use invalidate() to mark the object as
43       stale.  An object is also automatically stale if it has not been updated
44       in `_poll_time` seconds.
45
46     * Record access time (atime) timestamp
47
48     * Manage internal use count used by the inode cache ("inc_use" and
49       "dec_use").  An object which is in use cannot be cleared by the inode
50       cache.
51
52     * Manage the kernel reference count ("inc_ref" and "dec_ref").  An object
53       which is referenced by the kernel cannot have its inode entry deleted.
54
55     * Record cache footprint, cache priority
56
57     * Record Arvados uuid at the time the object is placed in the cache
58
59     * Clear the object contents (invalidates the object)
60
61     """
62
63     __slots__ = ("_stale", "_poll", "_last_update", "_atime", "_poll_time", "use_count",
64                  "ref_count", "dead", "cache_size", "cache_uuid", "allow_attr_cache")
65
66     def __init__(self):
67         self._stale = True
68         self._poll = False
69         self._last_update = time.time()
70         self._atime = time.time()
71         self._poll_time = 60
72         self.use_count = 0
73         self.ref_count = 0
74         self.dead = False
75         self.cache_size = 0
76         self.cache_uuid = None
77
78         # Can the kernel cache attributes?
79         self.allow_attr_cache = True
80
81     def invalidate(self):
82         """Indicate that object contents should be refreshed from source."""
83         self._stale = True
84
85     def kernel_invalidate(self):
86         """Indicate that an invalidation for this object should be sent to the kernel."""
87         pass
88
89     # Test if the entries dict is stale.
90     def stale(self):
91         if self._stale:
92             return True
93         if self._poll:
94             return (self._last_update + self._poll_time) < self._atime
95         return False
96
97     def fresh(self):
98         self._stale = False
99         self._last_update = time.time()
100
101     def atime(self):
102         return self._atime
103
104     def persisted(self):
105         return False
106
107     def clear(self):
108         pass
109
110     def in_use(self):
111         return self.use_count > 0
112
113     def inc_use(self):
114         self.use_count += 1
115
116     def dec_use(self):
117         self.use_count -= 1
118
119     def inc_ref(self):
120         self.ref_count += 1
121         return self.ref_count
122
123     def dec_ref(self, n):
124         self.ref_count -= n
125         return self.ref_count
126
127     def has_ref(self, only_children):
128         """Determine if there are any kernel references to this
129         object or its children.
130
131         If only_children is True, ignore refcount of self and only consider
132         children.
133         """
134         if only_children:
135             return False
136         else:
137             return self.ref_count > 0
138
139     def objsize(self):
140         return 0
141
142     def uuid(self):
143         return None
144
145     def finalize(self):
146         pass
147
148     def child_event(self, ev):
149         pass
150
151     def time_to_next_poll(self):
152         if self._poll:
153             t = (self._last_update + self._poll_time) - self._atime
154             if t < 0:
155                 return 0
156             else:
157                 return t
158         else:
159             return self._poll_time