]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/monitor.py
Change deep-size/stats/check/manifest to a start+poll model instead of a single long...
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / monitor.py
1
2 from zope.interface import Interface, implements
3 from allmydata.util import observer
4
5 class IMonitor(Interface):
6     """I manage status, progress, and cancellation for long-running operations.
7
8     Whoever initiates the operation should create a Monitor instance and pass
9     it into the code that implements the operation. That code should
10     periodically check in with the Monitor, perhaps after each major unit of
11     work has been completed, for two purposes.
12
13     The first is to inform the Monitor about progress that has been made, so
14     that external observers can be reassured that the operation is proceeding
15     normally. If the operation has a well-known amount of work to perform,
16     this notification should reflect that, so that an ETA or 'percentage
17     complete' value can be derived.
18
19     The second purpose is to check to see if the operation has been
20     cancelled. The impatient observer who no longer wants the operation to
21     continue will inform the Monitor; the next time the operation code checks
22     in, it should notice that the operation has been cancelled, and wrap
23     things up. The same monitor can be passed to multiple operations, all of
24     which may check for cancellation: this pattern may be simpler than having
25     the original caller keep track of subtasks and cancel them individually.
26     """
27
28     # the following methods are provided for the operation code
29
30     def is_cancelled(self):
31         """Returns True if the operation has been cancelled. If True,
32         operation code should stop creating new work, and attempt to stop any
33         work already in progress."""
34
35     def set_status(self, status):
36         """Sets the Monitor's 'status' object to an arbitrary value.
37         Different operations will store different sorts of status information
38         here. Operation code should use get+modify+set sequences to update
39         this."""
40
41     def get_status(self):
42         """Return the status object."""
43
44     def finish(self, status):
45         """Call this when the operation is done, successful or not. The
46         Monitor's lifetime is influenced by the completion of the operation
47         it is monitoring. The Monitor's 'status' value will be set with the
48         'status' argument, just as if it had been passed to set_status().
49         This value will be used to fire the Deferreds that are returned by
50         when_done().
51
52         Operations that fire a Deferred when they finish should trigger this
53         with d.addBoth(monitor.finish)"""
54
55     # the following methods are provided for the initiator of the operation
56
57     def is_finished(self):
58         """Return a boolean, True if the operation is done (whether
59         successful or failed), False if it is still running."""
60
61     def when_done(self):
62         """Return a Deferred that fires when the operation is complete. It
63         will fire with the operation status, the same value as returned by
64         get_status()."""
65
66     def cancel(self):
67         """Cancel the operation as soon as possible. is_cancelled() will
68         start returning True after this is called."""
69
70     #   get_status() is useful too, but it is operation-specific
71
72 class Monitor:
73     implements(IMonitor)
74
75     def __init__(self):
76         self.cancelled = False
77         self.finished = False
78         self.status = None
79         self.observer = observer.OneShotObserverList()
80
81     def is_cancelled(self):
82         return self.cancelled
83
84     def is_finished(self):
85         return self.finished
86
87     def when_done(self):
88         return self.observer.when_fired()
89
90     def cancel(self):
91         self.cancelled = True
92
93     def finish(self, status_or_failure):
94         self.set_status(status_or_failure)
95         self.finished = True
96         self.observer.fire(status_or_failure)
97         return status_or_failure
98
99     def get_status(self):
100         return self.status
101     def set_status(self, status):
102         self.status = status
103
104 class MonitorTable:
105     def __init__(self):
106         self.handles = {} # maps ophandle (an arbitrary string) to a Monitor
107         # TODO: all timeouts, handle lifetime, retain-for=, etc, goes here.
108         # self.handles should probably be a WeakValueDictionary, and we need
109         # a table of timers, and operations which have finished should be
110         # handled slightly differently.
111
112     def get_monitor(self, handle):
113         return self.handles.get(handle)
114
115     def add_monitor(self, handle, monitor):
116         self.handles[handle] = monitor
117
118     def delete_monitor(self, handle):
119         if handle in self.handles:
120             del self.handles[handle]