]> mj.ucw.cz Git - libucw.git/blob - ucw/fb-file.c
Solved mainloop race by selfpipe
[libucw.git] / ucw / fb-file.c
1 /*
2  *      UCW Library -- Fast Buffered I/O on Files
3  *
4  *      (c) 1997--2007 Martin Mares <mj@ucw.cz>
5  *      (c) 2007 Pavel Charvat <pchar@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "ucw/lib.h"
12 #include "ucw/fastbuf.h"
13 #include "ucw/lfs.h"
14
15 #include <stdio.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19
20 struct fb_file {
21   struct fastbuf fb;
22   int fd;                               /* File descriptor */
23   int is_temp_file;
24   int keep_back_buf;                    /* Optimize for backwards reading */
25   ucw_off_t wpos;                       /* Real file position */
26   uns wlen;                             /* Window size */
27 };
28 #define FB_FILE(f) ((struct fb_file *)(f)->is_fastbuf)
29 #define FB_BUFFER(f) (byte *)(FB_FILE(f) + 1)
30
31 static int
32 bfd_refill(struct fastbuf *f)
33 {
34   struct fb_file *F = FB_FILE(f);
35   byte *read_ptr = (f->buffer = FB_BUFFER(f));
36   uns blen = f->bufend - f->buffer, back = F->keep_back_buf ? blen >> 2 : 0, read_len = blen;
37   /* Forward or no seek */
38   if (F->wpos <= f->pos)
39     {
40       ucw_off_t diff = f->pos - F->wpos;
41       /* Formula for long forward seeks (prefer lseek()) */
42       if (diff > ((ucw_off_t)blen << 2))
43         {
44 long_seek:
45           f->bptr = f->buffer + back;
46           f->bstop = f->buffer + blen;
47           goto seek;
48         }
49       /* Short forward seek (prefer read() to skip data )*/
50       else if ((uns)diff >= back)
51         {
52           uns skip = diff - back;
53           F->wpos += skip;
54           while (skip)
55             {
56               int l = read(F->fd, f->buffer, MIN(skip, blen));
57               if (unlikely(l <= 0))
58                 if (l < 0)
59                   die("Error reading %s: %m", f->name);
60                 else
61                   {
62                     F->wpos -= skip;
63                     goto eof;
64                   }
65               skip -= l;
66             }
67         }
68       /* Reuse part of the previous window and append new data (also F->wpos == f->pos) */
69       else
70         {
71           uns keep = back - (uns)diff;
72           if (keep >= F->wlen)
73             back = diff + (keep = F->wlen);
74           else
75             memmove(f->buffer, f->buffer + F->wlen - keep, keep);
76           read_len -= keep;
77           read_ptr += keep;
78         }
79       f->bptr = f->buffer + back;
80       f->bstop = f->buffer + blen;
81     }
82   /* Backwards seek */
83   else
84     {
85       ucw_off_t diff = F->wpos - f->pos;
86       /* Formula for long backwards seeks (keep smaller backbuffer than for shorter seeks ) */
87       if (diff > ((ucw_off_t)blen << 1))
88         {
89           if ((ucw_off_t)back > f->pos)
90             back = f->pos;
91           goto long_seek;
92         }
93       /* Seek into previous window (do nothing... for example brewind) */
94       else if ((uns)diff <= F->wlen) 
95         {
96           f->bstop = f->buffer + F->wlen;
97           f->bptr = f->bstop - diff;
98           f->pos = F->wpos;
99           return 1;
100         }
101       back *= 3;
102       if ((ucw_off_t)back > f->pos)
103         back = f->pos;
104       f->bptr = f->buffer + back;
105       read_len = blen;
106       f->bstop = f->buffer + read_len;
107       /* Reuse part of previous window */
108       if (F->wlen && read_len <= back + diff && read_len > back + diff - F->wlen)
109         {
110           uns keep = read_len + F->wlen - back - diff;
111           memmove(f->buffer + read_len - keep, f->buffer, keep);
112         }
113 seek:
114       /* Do lseek() */
115       F->wpos = f->pos + (f->buffer - f->bptr);
116       if (ucw_seek(F->fd, F->wpos, SEEK_SET) < 0)
117         die("Error seeking %s: %m", f->name);
118     }
119   /* Read (part of) buffer */
120   do
121     {
122       int l = read(F->fd, read_ptr, read_len);
123       if (unlikely(l < 0))
124         die("Error reading %s: %m", f->name);
125       if (!l)
126         if (unlikely(read_ptr < f->bptr))
127           goto eof;
128         else
129           break; /* Incomplete read because of EOF */
130       read_ptr += l;
131       read_len -= l;
132       F->wpos += l;
133     }
134   while (read_ptr <= f->bptr);
135   if (read_len)
136     f->bstop = read_ptr;
137   f->pos += f->bstop - f->bptr;
138   F->wlen = f->bstop - f->buffer;
139   return f->bstop - f->bptr;
140 eof:
141   /* Seeked behind EOF */
142   f->bptr = f->bstop = f->buffer;
143   F->wlen = 0;
144   return 0;
145 }
146
147 static void
148 bfd_spout(struct fastbuf *f)
149 {
150   /* Do delayed lseek() if needed */
151   if (FB_FILE(f)->wpos != f->pos && ucw_seek(FB_FILE(f)->fd, f->pos, SEEK_SET) < 0)
152     die("Error seeking %s: %m", f->name);
153
154   int l = f->bptr - f->buffer;
155   byte *c = f->buffer;
156
157   /* Write the buffer */
158   FB_FILE(f)->wpos = (f->pos += l);
159   FB_FILE(f)->wlen = 0;
160   while (l)
161     {
162       int z = write(FB_FILE(f)->fd, c, l);
163       if (z <= 0)
164         die("Error writing %s: %m", f->name);
165       l -= z;
166       c += z;
167     }
168   f->bptr = f->buffer = FB_BUFFER(f);
169 }
170
171 static int
172 bfd_seek(struct fastbuf *f, ucw_off_t pos, int whence)
173 {
174   /* Delay the seek for the next refill() or spout() call (if whence != SEEK_END). */
175   ucw_off_t l;
176   switch (whence)
177     {
178       case SEEK_SET:
179         f->pos = pos;
180         return 1;
181       case SEEK_CUR:
182         l = f->pos + pos;
183         if ((pos > 0) ^ (l > f->pos))
184           return 0;
185         f->pos = l;
186         return 1;
187       case SEEK_END:
188         l = ucw_seek(FB_FILE(f)->fd, pos, SEEK_END);
189         if (l < 0)
190           return 0;
191         FB_FILE(f)->wpos = f->pos = l;
192         FB_FILE(f)->wlen = 0;
193         return 1;
194       default:
195         ASSERT(0);
196     }
197 }
198
199 static void
200 bfd_close(struct fastbuf *f)
201 {
202   bclose_file_helper(f, FB_FILE(f)->fd, FB_FILE(f)->is_temp_file);
203   xfree(f);
204 }
205
206 static int
207 bfd_config(struct fastbuf *f, uns item, int value)
208 {
209   int orig;
210
211   switch (item)
212     {
213       case BCONFIG_IS_TEMP_FILE:
214         orig = FB_FILE(f)->is_temp_file;
215         FB_FILE(f)->is_temp_file = value;
216         return orig;
217       case BCONFIG_KEEP_BACK_BUF:
218         orig = FB_FILE(f)->keep_back_buf;
219         FB_FILE(f)->keep_back_buf = value;
220         return orig;
221       default:
222         return -1;
223     }
224 }
225
226 struct fastbuf *
227 bfdopen_internal(int fd, const char *name, uns buflen)
228 {
229   ASSERT(buflen);
230   int namelen = strlen(name) + 1;
231   struct fb_file *F = xmalloc_zero(sizeof(struct fb_file) + buflen + namelen);
232   struct fastbuf *f = &F->fb;
233
234   bzero(F, sizeof(*F));
235   f->buffer = (byte *)(F+1);
236   f->bptr = f->bstop = f->buffer;
237   f->bufend = f->buffer + buflen;
238   f->name = f->bufend;
239   memcpy(f->name, name, namelen);
240   F->fd = fd;
241   f->refill = bfd_refill;
242   f->spout = bfd_spout;
243   f->seek = bfd_seek;
244   f->close = bfd_close;
245   f->config = bfd_config;
246   f->can_overwrite_buffer = 2;
247   return f;
248 }
249
250 void
251 bfilesync(struct fastbuf *b)
252 {
253   bflush(b);
254   if (fsync(FB_FILE(b)->fd) < 0)
255     msg(L_ERROR, "fsync(%s) failed: %m", b->name);
256 }
257
258 #ifdef TEST
259
260 int main(void)
261 {
262   struct fastbuf *f, *t;
263   f = bopen_tmp(16);
264   t = bfdopen_shared(1, 13);
265   for (uns i = 0; i < 16; i++)
266     bwrite(f, "<hello>", 7);
267   bprintf(t, "%d\n", (int)btell(f));
268   brewind(f);
269   bbcopy(f, t, ~0U);
270   bprintf(t, "\n%d %d\n", (int)btell(f), (int)btell(t));
271   bclose(f);
272   bclose(t);
273   return 0;
274 }
275
276 #endif