2 * UCW Library -- Atomic Buffered Write to Files
4 * (c) 2006 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU Lesser General Public License.
11 #include <ucw/fastbuf.h>
14 #include <ucw/trans.h>
24 static struct cf_section fbatomic_config = {
26 CF_UNS("Trace", &trace)
30 static void CONSTRUCTOR fbatomic_init_config(void)
32 cf_declare_section("FBAtomic", &fbatomic_config, 1);
37 #define FB_ATOMIC(f) ((struct fb_atomic *)(f))
38 #define TRACE(m...) do { if(trace) msg(L_DEBUG, "FB_ATOMIC: " m); } while(0)
40 struct fb_atomic_file {
49 fbatomic_internal_write(struct fastbuf *f)
51 struct fb_atomic_file *af = FB_ATOMIC(f)->af;
52 int size = f->bptr - f->buffer;
55 ASSERT(af->record_len < 0 || !(size % af->record_len));
56 int res = write(af->fd, f->buffer, size);
58 bthrow(f, "write", "Error writing %s: %m", f->name);
60 bthrow(f, "write", "Unexpected partial write to %s: written only %d bytes of %d", f->name, res, size);
66 fbatomic_spout(struct fastbuf *f)
68 if (f->bptr < f->bufend) /* Explicit flushes should be ignored */
71 struct fb_atomic *F = FB_ATOMIC(f);
74 uns written = f->bptr - f->buffer;
75 uns size = f->bufend - f->buffer + F->slack_size;
77 TRACE("Reallocating buffer for atomic file %s with slack %d", f->name, F->slack_size);
78 f->buffer = xrealloc(f->buffer, size);
79 f->bufend = f->buffer + size;
80 f->bptr = f->buffer + written;
81 F->expected_max_bptr = f->bufend - F->slack_size;
84 fbatomic_internal_write(f);
88 fbatomic_close(struct fastbuf *f)
90 struct fb_atomic_file *af = FB_ATOMIC(f)->af;
91 if (!(f->flags & FB_DEAD))
92 fbatomic_internal_write(f); /* Need to flush explicitly, because the file can be locked */
102 fbatomic_open(const char *name, struct fastbuf *master, uns bufsize, int record_len)
104 struct fb_atomic *F = xmalloc_zero(sizeof(*F));
105 struct fastbuf *f = &F->fb;
106 struct fb_atomic_file *af;
109 af = FB_ATOMIC(master)->af;
111 ASSERT(af->record_len == record_len);
115 int fd = ucw_open(name, O_WRONLY | O_CREAT | O_TRUNC | O_APPEND, 0666);
117 trans_throw("ucw.fb.open", NULL, "Cannot create %s: %m", name);
118 af = xmalloc_zero(sizeof(*af) + strlen(name));
121 af->record_len = record_len;
122 af->locked = (record_len < 0);
123 strcpy(af->name, name);
126 if (record_len > 0 && bufsize % record_len)
127 bufsize += record_len - (bufsize % record_len);
128 f->buffer = xmalloc(bufsize);
129 f->bufend = f->buffer + bufsize;
130 F->slack_size = (record_len < 0) ? -record_len : 0;
131 ASSERT(bufsize > F->slack_size);
132 F->expected_max_bptr = f->bufend - F->slack_size;
133 f->bptr = f->bstop = f->buffer;
135 f->spout = fbatomic_spout;
136 f->close = fbatomic_close;
142 int main(int argc UNUSED, char **argv UNUSED)
144 struct fastbuf *f, *g;
146 // Always trace in the test
149 msg(L_INFO, "Testing block writes");
150 f = fbatomic_open("test", NULL, 16, 4);
151 for (u32 i=0; i<17; i++)
155 msg(L_INFO, "Testing interleaved var-size writes");
156 f = fbatomic_open("test2", NULL, 23, -5);
157 g = fbatomic_open("test2", f, 23, -5);
158 for (int i=0; i<100; i++)
160 struct fastbuf *x = (i%2) ? g : f;
161 bprintf(x, "%c<%d>\n", "fg"[i%2], ((259309*i) % 1000000) >> (i % 8));