(refs #318) simplify slave shutdown; doesn't fix dangling reference
[buildbot.git] / buildbot / dnotify.py
blobd23d6005d5729f3d047dcf9373c5105b1bd1a4d4
2 # spiv wants this
4 import fcntl, signal
6 class DNotify_Handler:
7 def __init__(self):
8 self.watchers = {}
9 self.installed = 0
10 def install(self):
11 if self.installed:
12 return
13 signal.signal(signal.SIGIO, self.fire)
14 self.installed = 1
15 def uninstall(self):
16 if not self.installed:
17 return
18 signal.signal(signal.SIGIO, signal.SIG_DFL)
19 self.installed = 0
20 def add(self, watcher):
21 self.watchers[watcher.fd.fileno()] = watcher
22 self.install()
23 def remove(self, watcher):
24 if self.watchers.has_key(watcher.fd.fileno()):
25 del(self.watchers[watcher.fd.fileno()])
26 if not self.watchers:
27 self.uninstall()
28 def fire(self, signum, frame):
29 # this is the signal handler
30 # without siginfo_t, we must fire them all
31 for watcher in self.watchers.values():
32 watcher.callback()
34 class DNotify:
35 DN_ACCESS = fcntl.DN_ACCESS # a file in the directory was read
36 DN_MODIFY = fcntl.DN_MODIFY # a file was modified (write,truncate)
37 DN_CREATE = fcntl.DN_CREATE # a file was created
38 DN_DELETE = fcntl.DN_DELETE # a file was unlinked
39 DN_RENAME = fcntl.DN_RENAME # a file was renamed
40 DN_ATTRIB = fcntl.DN_ATTRIB # a file had attributes changed (chmod,chown)
42 handler = [None]
44 def __init__(self, dirname, callback=None,
45 flags=[DN_MODIFY,DN_CREATE,DN_DELETE,DN_RENAME]):
47 """This object watches a directory for changes. The .callback
48 attribute should be set to a function to be run every time something
49 happens to it. Be aware that it will be called more times than you
50 expect."""
52 if callback:
53 self.callback = callback
54 else:
55 self.callback = self.fire
56 self.dirname = dirname
57 self.flags = reduce(lambda x, y: x | y, flags) | fcntl.DN_MULTISHOT
58 self.fd = open(dirname, "r")
59 # ideally we would move the notification to something like SIGRTMIN,
60 # (to free up SIGIO) and use sigaction to have the signal handler
61 # receive a structure with the fd number. But python doesn't offer
62 # either.
63 if not self.handler[0]:
64 self.handler[0] = DNotify_Handler()
65 self.handler[0].add(self)
66 fcntl.fcntl(self.fd, fcntl.F_NOTIFY, self.flags)
67 def remove(self):
68 self.handler[0].remove(self)
69 self.fd.close()
70 def fire(self):
71 print self.dirname, "changed!"
73 def test_dnotify1():
74 d = DNotify(".")
75 while 1:
76 signal.pause()
78 def test_dnotify2():
79 # create ./foo/, create/delete files in ./ and ./foo/ while this is
80 # running. Notice how both notifiers are fired when anything changes;
81 # this is an unfortunate side-effect of the lack of extended sigaction
82 # support in Python.
83 count = [0]
84 d1 = DNotify(".")
85 def fire1(count=count, d1=d1):
86 print "./ changed!", count[0]
87 count[0] += 1
88 if count[0] > 5:
89 d1.remove()
90 del(d1)
91 # change the callback, since we can't define it until after we have the
92 # dnotify object. Hmm, unless we give the dnotify to the callback.
93 d1.callback = fire1
94 def fire2(): print "foo/ changed!"
95 d2 = DNotify("foo", fire2)
96 while 1:
97 signal.pause()
100 if __name__ == '__main__':
101 test_dnotify2()