##// END OF EJS Templates
work around python bug on solaris 10....
Vadim Gelfer -
r2027:94d31703 default
parent child Browse files
Show More
@@ -1,171 +1,172
1 1 # appendfile.py - special classes to make repo updates atomic
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import *
9 9 demandload(globals(), "cStringIO changelog manifest os tempfile")
10 10
11 11 # writes to metadata files are ordered. reads: changelog, manifest,
12 12 # normal files. writes: normal files, manifest, changelog.
13 13
14 14 # manifest contains pointers to offsets in normal files. changelog
15 15 # contains pointers to offsets in manifest. if reader reads old
16 16 # changelog while manifest or normal files are written, it has no
17 17 # pointers into new parts of those files that are maybe not consistent
18 18 # yet, so will not read them.
19 19
20 20 # localrepo.addchangegroup thinks it writes changelog first, then
21 21 # manifest, then normal files (this is order they are available, and
22 22 # needed for computing linkrev fields), but uses appendfile to hide
23 23 # updates from readers. data not written to manifest or changelog
24 24 # until all normal files updated. write manifest first, then
25 25 # changelog.
26 26
27 27 # with this write ordering, readers cannot see inconsistent view of
28 28 # repo during update.
29 29
30 30 class appendfile(object):
31 31 '''implement enough of file protocol to append to revlog file.
32 32 appended data is written to temp file. reads and seeks span real
33 33 file and temp file. readers cannot see appended data until
34 34 writedata called.'''
35 35
36 36 def __init__(self, fp):
37 37 fd, self.tmpname = tempfile.mkstemp()
38 38 self.tmpfp = os.fdopen(fd, 'ab+')
39 39 self.realfp = fp
40 40 self.offset = fp.tell()
41 41 # real file is not written by anyone else. cache its size so
42 42 # seek and read can be fast.
43 43 self.fpsize = os.fstat(fp.fileno()).st_size
44 44
45 45 def seek(self, offset):
46 46 '''virtual file offset spans real file and temp file.'''
47 47 self.offset = offset
48 48 if self.offset < self.fpsize:
49 49 self.realfp.seek(self.offset)
50 50 else:
51 51 self.tmpfp.seek(self.offset - self.fpsize)
52 52
53 53 def read(self, count=-1):
54 54 '''only trick here is reads that span real file and temp file.'''
55 55 fp = cStringIO.StringIO()
56 56 old_offset = self.offset
57 57 if self.offset < self.fpsize:
58 58 s = self.realfp.read(count)
59 59 fp.write(s)
60 60 self.offset += len(s)
61 61 if count > 0:
62 62 count -= len(s)
63 63 if count != 0:
64 64 if old_offset != self.offset:
65 65 self.tmpfp.seek(self.offset - self.fpsize)
66 66 s = self.tmpfp.read(count)
67 67 fp.write(s)
68 68 self.offset += len(s)
69 69 return fp.getvalue()
70 70
71 71 def write(self, s):
72 72 '''append to temp file.'''
73 self.tmpfp.seek(0, 2)
73 74 self.tmpfp.write(s)
74 75 # all writes are appends, so offset must go to end of file.
75 76 self.offset = self.fpsize + self.tmpfp.tell()
76 77
77 78 def writedata(self):
78 79 '''copy data from temp file to real file.'''
79 80 self.tmpfp.seek(0)
80 81 s = self.tmpfp.read()
81 82 self.tmpfp.close()
82 83 self.realfp.seek(0, 2)
83 84 # small race here. we write all new data in one call, but
84 85 # reader can see partial update due to python or os. file
85 86 # locking no help: slow, not portable, not reliable over nfs.
86 87 # only safe thing is write to temp file every time and rename,
87 88 # but performance bad when manifest or changelog gets big.
88 89 self.realfp.write(s)
89 90 self.realfp.close()
90 91
91 92 def __del__(self):
92 93 '''delete temp file even if exception raised.'''
93 94 try: os.unlink(self.tmpname)
94 95 except: pass
95 96
96 97 class sharedfile(object):
97 98 '''let file objects share a single appendfile safely. each
98 99 sharedfile has own offset, syncs up with appendfile offset before
99 100 read and after read and write.'''
100 101
101 102 def __init__(self, fp):
102 103 self.fp = fp
103 104 self.offset = 0
104 105
105 106 def seek(self, offset):
106 107 self.offset = offset
107 108
108 109 def read(self, count=-1):
109 110 try:
110 111 if self.offset != self.fp.offset:
111 112 self.fp.seek(self.offset)
112 113 return self.fp.read(count)
113 114 finally:
114 115 self.offset = self.fp.offset
115 116
116 117 def write(self, s):
117 118 try:
118 119 return self.fp.write(s)
119 120 finally:
120 121 self.offset = self.fp.offset
121 122
122 123 def close(self):
123 124 # revlog wants this.
124 125 pass
125 126
126 127 def flush(self):
127 128 # revlog wants this.
128 129 pass
129 130
130 131 def writedata(self):
131 132 self.fp.writedata()
132 133
133 134 class appendopener(object):
134 135 '''special opener for files that only read or append.'''
135 136
136 137 def __init__(self, opener):
137 138 self.realopener = opener
138 139 # key: file name, value: appendfile object
139 140 self.fps = {}
140 141
141 142 def __call__(self, name, mode='r'):
142 143 '''open file. return same cached appendfile object for every
143 144 later call.'''
144 145
145 146 assert mode in 'ra'
146 147 fp = self.fps.get(name)
147 148 if fp is None:
148 149 fp = appendfile(self.realopener(name, 'a+'))
149 150 self.fps[name] = fp
150 151 return sharedfile(fp)
151 152
152 153 def writedata(self):
153 154 '''copy data from temp files to real files.'''
154 155 # write .d file before .i file.
155 156 fps = self.fps.items()
156 157 fps.sort()
157 158 for name, fp in fps:
158 159 fp.writedata()
159 160
160 161 # files for changelog and manifest are in different appendopeners, so
161 162 # not mixed up together.
162 163
163 164 class appendchangelog(changelog.changelog, appendopener):
164 165 def __init__(self, opener):
165 166 appendopener.__init__(self, opener)
166 167 changelog.changelog.__init__(self, self)
167 168
168 169 class appendmanifest(manifest.manifest, appendopener):
169 170 def __init__(self, opener):
170 171 appendopener.__init__(self, opener)
171 172 manifest.manifest.__init__(self, self)
General Comments 0
You need to be logged in to leave comments. Login now