1implement Logfile; 2 3# 4# Copyright © 1999 Vita Nuova Limited. All rights reserved. 5# 6 7include "sys.m"; 8 sys: Sys; 9 stderr: ref Sys->FD; 10include "draw.m"; 11 12Logfile: module { 13 init: fn(nil: ref Draw->Context, argv: list of string); 14}; 15 16Fidrec: adt { 17 fid: int; # fid of read 18 rq: list of (int, Sys->Rread); # outstanding read requests 19 pos: int; # current position in the logfile 20}; 21 22Circbuf: adt { 23 start: int; 24 data: array of byte; 25 new: fn(size: int): ref Circbuf; 26 put: fn(b: self ref Circbuf, d: array of byte): int; 27 get: fn(b: self ref Circbuf, s, n: int): (int, array of byte); 28}; 29 30Fidhash: adt 31{ 32 table: array of list of ref Fidrec; 33 get: fn(ht: self ref Fidhash, fid: int): ref Fidrec; 34 put: fn(ht: self ref Fidhash, fidrec: ref Fidrec); 35 del: fn(ht: self ref Fidhash, fidrec: ref Fidrec); 36 new: fn(): ref Fidhash; 37}; 38 39usage() 40{ 41 sys->fprint(stderr, "usage: logfile [-size] file\n"); 42 raise "fail: usage"; 43} 44 45init(nil: ref Draw->Context, argv: list of string) 46{ 47 sys = load Sys Sys->PATH; 48 stderr = sys->fildes(2); 49 50 bufsize := Sys->ATOMICIO * 4; 51 52 if (argv != nil) 53 argv = tl argv; 54 if (argv != nil && len hd argv && (hd argv)[0] == '-' && len hd argv > 1) { 55 if ((bufsize = int ((hd argv)[1:])) <= 0) { 56 sys->fprint(stderr, "logfile: can't have a zero buffer size\n"); 57 usage(); 58 } 59 argv = tl argv; 60 } 61 if (argv == nil || tl argv != nil) 62 usage(); 63 path := hd argv; 64 65 (dir, f) := pathsplit(path); 66 if (sys->bind("#s", dir, Sys->MBEFORE) == -1) { 67 sys->fprint(stderr, "logfile: bind #s failed: %r\n"); 68 return; 69 } 70 fio := sys->file2chan(dir, f); 71 if (fio == nil) { 72 sys->fprint(stderr, "logfile: couldn't make %s: %r\n", path); 73 return; 74 } 75 76 spawn logserver(fio, bufsize); 77} 78 79logserver(fio: ref Sys->FileIO, bufsize: int) 80{ 81 waitlist: list of ref Fidrec; 82 readers := Fidhash.new(); 83 availcount := 0; 84 availchan := chan of int; 85 workchan := chan of (Sys->Rread, array of byte); 86 buf := Circbuf.new(bufsize); 87 for (;;) alt { 88 <-availchan => 89 availcount++; 90 (nil, count, fid, rc) := <-fio.read => 91 r := readers.get(fid); 92 if (rc == nil) { 93 if (r != nil) 94 readers.del(r); 95 continue; 96 } 97 if (r == nil) { 98 r = ref Fidrec(fid, nil, buf.start); 99 if (r.pos < len buf.data) 100 r.pos = len buf.data; # first buffer's worth is garbage 101 readers.put(r); 102 } 103 104 (s, d) := buf.get(r.pos, count); 105 r.pos = s + len d; 106 107 if (d != nil) { 108 rc <-= (d, nil); 109 } else { 110 if (r.rq == nil) 111 waitlist = r :: waitlist; 112 r.rq = (count, rc) :: r.rq; 113 } 114 115 (nil, data, nil, wc) := <-fio.write => 116 if (wc == nil) 117 continue; 118 if ((n := buf.put(data)) < len data) 119 wc <-= (n, "write too long for buffer"); 120 else 121 wc <-= (n, nil); 122 123 wl := waitlist; 124 for (waitlist = nil; wl != nil; wl = tl wl) { 125 r := hd wl; 126 if (availcount == 0) { 127 spawn worker(workchan, availchan); 128 availcount++; 129 } 130 (count, rc) := hd r.rq; 131 r.rq = tl r.rq; 132 133 # optimisation: if the read request wants exactly the data provided 134 # in the write request, then use the original data buffer. 135 s: int; 136 d: array of byte; 137 if (count >= n && r.pos == buf.start + len buf.data - n) 138 (s, d) = (r.pos, data); 139 else 140 (s, d) = buf.get(r.pos, count); 141 r.pos = s + len d; 142 workchan <-= (rc, d); 143 availcount--; 144 if (r.rq != nil) 145 waitlist = r :: waitlist; 146 d = nil; 147 } 148 data = nil; 149 wl = nil; 150 } 151} 152 153worker(work: chan of (Sys->Rread, array of byte), ready: chan of int) 154{ 155 for (;;) { 156 (rc, data) := <-work; # blocks forever if the reading process is killed 157 rc <-= (data, nil); 158 (rc, data) = (nil, nil); 159 ready <-= 1; 160 } 161} 162 163Circbuf.new(size: int): ref Circbuf 164{ 165 return ref Circbuf(0, array[size] of byte); 166} 167 168# return number of bytes actually written 169Circbuf.put(b: self ref Circbuf, d: array of byte): int 170{ 171 blen := len b.data; 172 # if too big to fit in buffer, truncate the write. 173 if (len d > blen) 174 d = d[0:blen]; 175 dlen := len d; 176 177 offset := b.start % blen; 178 if (offset + dlen <= blen) { 179 b.data[offset:] = d; 180 } else { 181 b.data[offset:] = d[0:blen - offset]; 182 b.data[0:] = d[blen - offset:]; 183 } 184 b.start += dlen; 185 return dlen; 186} 187 188# return (start, data) 189Circbuf.get(b: self ref Circbuf, s, n: int): (int, array of byte) 190{ 191 # if the beginning's been overrun, start from the earliest place we can. 192 # we could put some indication of elided bytes in the buffer. 193 if (s < b.start) 194 s = b.start; 195 blen := len b.data; 196 if (s + n > b.start + blen) 197 n = b.start + blen - s; 198 if (n <= 0) 199 return (s, nil); 200 o := s % blen; 201 d := array[n] of byte; 202 if (o + n <= blen) 203 d[0:] = b.data[o:o+n]; 204 else { 205 d[0:] = b.data[o:]; 206 d[blen - o:] = b.data[0:o+n-blen]; 207 } 208 return (s, d); 209} 210 211FIDHASHSIZE: con 32; 212 213Fidhash.new(): ref Fidhash 214{ 215 return ref Fidhash(array[FIDHASHSIZE] of list of ref Fidrec); 216} 217 218# put an entry in the hash table. 219# assumes there is no current entry for the fid. 220Fidhash.put(ht: self ref Fidhash, f: ref Fidrec) 221{ 222 slot := f.fid & (FIDHASHSIZE-1); 223 ht.table[slot] = f :: ht.table[slot]; 224} 225 226Fidhash.get(ht: self ref Fidhash, fid: int): ref Fidrec 227{ 228 for (l := ht.table[fid & (FIDHASHSIZE-1)]; l != nil; l = tl l) 229 if ((hd l).fid == fid) 230 return hd l; 231 return nil; 232} 233 234Fidhash.del(ht: self ref Fidhash, f: ref Fidrec) 235{ 236 slot := f.fid & (FIDHASHSIZE-1); 237 nl: list of ref Fidrec; 238 for (l := ht.table[slot]; l != nil; l = tl l) 239 if ((hd l).fid != f.fid) 240 nl = (hd l) :: nl; 241 ht.table[slot] = nl; 242} 243 244pathsplit(p: string): (string, string) 245{ 246 for (i := len p - 1; i >= 0; i--) 247 if (p[i] != '/') 248 break; 249 if (i < 0) 250 return (p, nil); 251 p = p[0:i+1]; 252 for (i = len p - 1; i >=0; i--) 253 if (p[i] == '/') 254 break; 255 if (i < 0) 256 return (".", p); 257 return (p[0:i+1], p[i+1:]); 258} 259 260