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