##// END OF EJS Templates
inotify: modular architecture for inotify clients...
Nicolas Dumazet -
r8551:7089d972 default
parent child Browse files
Show More
@@ -1,125 +1,127
1 1 # __init__.py - inotify-based status acceleration for Linux
2 2 #
3 3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
4 4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 9 '''inotify-based status acceleration for Linux systems
10 10 '''
11 11
12 12 # todo: socket permissions
13 13
14 14 from mercurial.i18n import _
15 15 from mercurial import cmdutil, util
16 import client, errno, os, server, socket
16 import errno, os, server, socket
17 17 from weakref import proxy
18 from client import client
18 19
19 20 def serve(ui, repo, **opts):
20 21 '''start an inotify server for this repository'''
21 22 timeout = opts.get('timeout')
22 23 if timeout:
23 24 timeout = float(timeout) * 1e3
24 25
25 26 class service:
26 27 def init(self):
27 28 try:
28 29 self.master = server.master(ui, repo, timeout)
29 30 except server.AlreadyStartedException, inst:
30 31 raise util.Abort(str(inst))
31 32
32 33 def run(self):
33 34 try:
34 35 self.master.run()
35 36 finally:
36 37 self.master.shutdown()
37 38
38 39 service = service()
39 40 cmdutil.service(opts, initfn=service.init, runfn=service.run)
40 41
41 42 def reposetup(ui, repo):
42 43 if not hasattr(repo, 'dirstate'):
43 44 return
44 45
45 46 # XXX: weakref until hg stops relying on __del__
46 47 repo = proxy(repo)
47 48
48 49 class inotifydirstate(repo.dirstate.__class__):
49 50 # Set to True if we're the inotify server, so we don't attempt
50 51 # to recurse.
51 52 inotifyserver = False
52 53
53 54 def status(self, match, ignored, clean, unknown=True):
54 55 files = match.files()
55 56 if '.' in files:
56 57 files = []
58 cli = client(ui, repo)
57 59 try:
58 60 if not ignored and not self.inotifyserver:
59 result = client.query(ui, repo, files, match, False,
61 result = cli.statusquery(files, match, False,
60 62 clean, unknown)
61 63 if result and ui.config('inotify', 'debug'):
62 64 r2 = super(inotifydirstate, self).status(
63 65 match, False, clean, unknown)
64 66 for c,a,b in zip('LMARDUIC', result, r2):
65 67 for f in a:
66 68 if f not in b:
67 69 ui.warn('*** inotify: %s +%s\n' % (c, f))
68 70 for f in b:
69 71 if f not in a:
70 72 ui.warn('*** inotify: %s -%s\n' % (c, f))
71 73 result = r2
72 74
73 75 if result is not None:
74 76 return result
75 77 except (OSError, socket.error), err:
76 78 autostart = ui.configbool('inotify', 'autostart', True)
77 79
78 80 if err[0] == errno.ECONNREFUSED:
79 81 ui.warn(_('(found dead inotify server socket; '
80 82 'removing it)\n'))
81 83 os.unlink(repo.join('inotify.sock'))
82 84 if err[0] in (errno.ECONNREFUSED, errno.ENOENT) and autostart:
83 85 ui.debug(_('(starting inotify server)\n'))
84 86 try:
85 87 try:
86 88 server.start(ui, repo)
87 89 except server.AlreadyStartedException, inst:
88 90 # another process may have started its own
89 91 # inotify server while this one was starting.
90 92 ui.debug(str(inst))
91 93 except Exception, inst:
92 94 ui.warn(_('could not start inotify server: '
93 95 '%s\n') % inst)
94 96 else:
95 97 # server is started, send query again
96 98 try:
97 return client.query(ui, repo, files, match,
98 ignored, clean, unknown)
99 return cli.statusquery(files, match, ignored,
100 clean, unknown)
99 101 except socket.error, err:
100 102 ui.warn(_('could not talk to new inotify '
101 103 'server: %s\n') % err[-1])
102 104 elif err[0] in (errno.ECONNREFUSED, errno.ENOENT):
103 105 # silently ignore normal errors if autostart is False
104 106 ui.debug(_('(inotify server not running)\n'))
105 107 else:
106 108 ui.warn(_('failed to contact inotify server: %s\n')
107 109 % err[-1])
108 110 ui.traceback()
109 111 # replace by old status function
110 112 self.status = super(inotifydirstate, self).status
111 113
112 114 return super(inotifydirstate, self).status(
113 115 match, ignored, clean, unknown)
114 116
115 117 repo.dirstate.__class__ = inotifydirstate
116 118
117 119 cmdtable = {
118 120 '^inserve':
119 121 (serve,
120 122 [('d', 'daemon', None, _('run server in background')),
121 123 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
122 124 ('t', 'idle-timeout', '', _('minutes to sit idle before exiting')),
123 125 ('', 'pid-file', '', _('name of file to write process ID to'))],
124 126 _('hg inserve [OPT]...')),
125 127 }
@@ -1,65 +1,94
1 1 # client.py - inotify status client
2 2 #
3 3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
4 4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
5 # Copyright 2009 Nicolas Dumazet <nicdumz@gmail.com>
5 6 #
6 7 # This software may be used and distributed according to the terms of the
7 8 # GNU General Public License version 2, incorporated herein by reference.
8 9
9 10 from mercurial.i18n import _
10 11 import common
11 12 import os, socket, struct
12 13
13 def query(ui, repo, names, match, ignored, clean, unknown=True):
14 sock = socket.socket(socket.AF_UNIX)
15 sockpath = repo.join('inotify.sock')
14 class client(object):
15 def __init__(self, ui, repo):
16 self.ui = ui
17 self.repo = repo
18 self.sock = socket.socket(socket.AF_UNIX)
19
20 def _connect(self):
21 sockpath = self.repo.join('inotify.sock')
16 22 try:
17 sock.connect(sockpath)
23 self.sock.connect(sockpath)
18 24 except socket.error, err:
19 25 if err[0] == "AF_UNIX path too long":
20 26 sockpath = os.readlink(sockpath)
21 sock.connect(sockpath)
27 self.sock.connect(sockpath)
22 28 else:
23 29 raise
24 30
31 def _send(self, data):
32 """Sends protocol version number, and the data"""
33 self.sock.sendall(chr(common.version) + data)
34
35 self.sock.shutdown(socket.SHUT_WR)
36
37 def _receive(self):
38 """
39 Read data, check version number, extract headers,
40 and returns a tuple (data descriptor, header)
41 Returns (None, None) on error
42 """
43 cs = common.recvcs(self.sock)
44 version = ord(cs.read(1))
45 if version != common.version:
46 self.ui.warn(_('(inotify: received response from incompatible '
47 'server version %d)\n') % version)
48 return None, None
49
50 # only one type of request is supported for now
51 type = 'STAT'
52 hdrfmt = common.resphdrfmts[type]
53 hdrsize = common.resphdrsizes[type]
54 try:
55 resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
56 except struct.error:
57 return None, None
58
59 return cs, resphdr
60
61 def query(self, req):
62 self._connect()
63
64 self._send(req)
65
66 return self._receive()
67
68 def statusquery(self, names, match, ignored, clean, unknown=True):
69
25 70 def genquery():
26 71 for n in names:
27 72 yield n
28 73 states = 'almrx!'
29 74 if ignored:
30 75 raise ValueError('this is insanity')
31 76 if clean: states += 'c'
32 77 if unknown: states += '?'
33 78 yield states
34 79
35 80 req = '\0'.join(genquery())
36 81
37 sock.sendall(chr(common.version))
38 sock.sendall(req)
39 sock.shutdown(socket.SHUT_WR)
40
41 cs = common.recvcs(sock)
42 version = ord(cs.read(1))
82 cs, resphdr = self.query(req)
43 83
44 if version != common.version:
45 ui.warn(_('(inotify: received response from incompatible server '
46 'version %d)\n') % version)
47 return None
48
49 # only one type of request is supported for now
50 type = 'STAT'
51 hdrfmt = common.resphdrfmts[type]
52 hdrsize = common.resphdrsizes[type]
53 try:
54 resphdr = struct.unpack(hdrfmt, cs.read(hdrsize))
55 except struct.error:
84 if not cs:
56 85 return None
57 86
58 87 def readnames(nbytes):
59 88 if nbytes:
60 89 names = cs.read(nbytes)
61 90 if names:
62 91 return filter(match, names.split('\0'))
63 92 return []
64 93
65 94 return map(readnames, resphdr)
General Comments 0
You need to be logged in to leave comments. Login now