137da2899SCharles.Forsythimplement Logfile; 237da2899SCharles.Forsyth 337da2899SCharles.Forsyth# 437da2899SCharles.Forsyth# Copyright © 1999 Vita Nuova Limited. All rights reserved. 537da2899SCharles.Forsyth# 637da2899SCharles.Forsyth 737da2899SCharles.Forsythinclude "sys.m"; 837da2899SCharles.Forsyth sys: Sys; 937da2899SCharles.Forsyth stderr: ref Sys->FD; 1037da2899SCharles.Forsythinclude "draw.m"; 1137da2899SCharles.Forsyth 1237da2899SCharles.ForsythLogfile: module { 1337da2899SCharles.Forsyth init: fn(nil: ref Draw->Context, argv: list of string); 1437da2899SCharles.Forsyth}; 1537da2899SCharles.Forsyth 1637da2899SCharles.ForsythFidrec: adt { 1737da2899SCharles.Forsyth fid: int; # fid of read 1837da2899SCharles.Forsyth rq: list of (int, Sys->Rread); # outstanding read requests 1937da2899SCharles.Forsyth pos: int; # current position in the logfile 2037da2899SCharles.Forsyth}; 2137da2899SCharles.Forsyth 2237da2899SCharles.ForsythCircbuf: adt { 2337da2899SCharles.Forsyth start: int; 2437da2899SCharles.Forsyth data: array of byte; 2537da2899SCharles.Forsyth new: fn(size: int): ref Circbuf; 2637da2899SCharles.Forsyth put: fn(b: self ref Circbuf, d: array of byte): int; 2737da2899SCharles.Forsyth get: fn(b: self ref Circbuf, s, n: int): (int, array of byte); 2837da2899SCharles.Forsyth}; 2937da2899SCharles.Forsyth 3037da2899SCharles.ForsythFidhash: adt 3137da2899SCharles.Forsyth{ 3237da2899SCharles.Forsyth table: array of list of ref Fidrec; 3337da2899SCharles.Forsyth get: fn(ht: self ref Fidhash, fid: int): ref Fidrec; 3437da2899SCharles.Forsyth put: fn(ht: self ref Fidhash, fidrec: ref Fidrec); 3537da2899SCharles.Forsyth del: fn(ht: self ref Fidhash, fidrec: ref Fidrec); 3637da2899SCharles.Forsyth new: fn(): ref Fidhash; 3737da2899SCharles.Forsyth}; 3837da2899SCharles.Forsyth 3937da2899SCharles.Forsythusage() 4037da2899SCharles.Forsyth{ 4137da2899SCharles.Forsyth sys->fprint(stderr, "usage: logfile [-size] file\n"); 4237da2899SCharles.Forsyth raise "fail: usage"; 4337da2899SCharles.Forsyth} 4437da2899SCharles.Forsyth 4537da2899SCharles.Forsythinit(nil: ref Draw->Context, argv: list of string) 4637da2899SCharles.Forsyth{ 4737da2899SCharles.Forsyth sys = load Sys Sys->PATH; 4837da2899SCharles.Forsyth stderr = sys->fildes(2); 4937da2899SCharles.Forsyth 5037da2899SCharles.Forsyth bufsize := Sys->ATOMICIO * 4; 5137da2899SCharles.Forsyth 5237da2899SCharles.Forsyth if (argv != nil) 5337da2899SCharles.Forsyth argv = tl argv; 5437da2899SCharles.Forsyth if (argv != nil && len hd argv && (hd argv)[0] == '-' && len hd argv > 1) { 5537da2899SCharles.Forsyth if ((bufsize = int ((hd argv)[1:])) <= 0) { 5637da2899SCharles.Forsyth sys->fprint(stderr, "logfile: can't have a zero buffer size\n"); 5737da2899SCharles.Forsyth usage(); 5837da2899SCharles.Forsyth } 5937da2899SCharles.Forsyth argv = tl argv; 6037da2899SCharles.Forsyth } 6137da2899SCharles.Forsyth if (argv == nil || tl argv != nil) 6237da2899SCharles.Forsyth usage(); 6337da2899SCharles.Forsyth path := hd argv; 6437da2899SCharles.Forsyth 6537da2899SCharles.Forsyth (dir, f) := pathsplit(path); 66*a3d4017dSCharles.Forsyth if (sys->bind("#s", dir, Sys->MBEFORE) == -1) { 6737da2899SCharles.Forsyth sys->fprint(stderr, "logfile: bind #s failed: %r\n"); 6837da2899SCharles.Forsyth return; 6937da2899SCharles.Forsyth } 7037da2899SCharles.Forsyth fio := sys->file2chan(dir, f); 7137da2899SCharles.Forsyth if (fio == nil) { 7237da2899SCharles.Forsyth sys->fprint(stderr, "logfile: couldn't make %s: %r\n", path); 7337da2899SCharles.Forsyth return; 7437da2899SCharles.Forsyth } 7537da2899SCharles.Forsyth 7637da2899SCharles.Forsyth spawn logserver(fio, bufsize); 7737da2899SCharles.Forsyth} 7837da2899SCharles.Forsyth 7937da2899SCharles.Forsythlogserver(fio: ref Sys->FileIO, bufsize: int) 8037da2899SCharles.Forsyth{ 8137da2899SCharles.Forsyth waitlist: list of ref Fidrec; 8237da2899SCharles.Forsyth readers := Fidhash.new(); 8337da2899SCharles.Forsyth availcount := 0; 8437da2899SCharles.Forsyth availchan := chan of int; 8537da2899SCharles.Forsyth workchan := chan of (Sys->Rread, array of byte); 8637da2899SCharles.Forsyth buf := Circbuf.new(bufsize); 8737da2899SCharles.Forsyth for (;;) alt { 8837da2899SCharles.Forsyth <-availchan => 8937da2899SCharles.Forsyth availcount++; 903f1f06c5SCharles.Forsyth (nil, count, fid, rc) := <-fio.read => 9137da2899SCharles.Forsyth r := readers.get(fid); 9237da2899SCharles.Forsyth if (rc == nil) { 9337da2899SCharles.Forsyth if (r != nil) 9437da2899SCharles.Forsyth readers.del(r); 9537da2899SCharles.Forsyth continue; 9637da2899SCharles.Forsyth } 9737da2899SCharles.Forsyth if (r == nil) { 9837da2899SCharles.Forsyth r = ref Fidrec(fid, nil, buf.start); 9937da2899SCharles.Forsyth if (r.pos < len buf.data) 10037da2899SCharles.Forsyth r.pos = len buf.data; # first buffer's worth is garbage 10137da2899SCharles.Forsyth readers.put(r); 10237da2899SCharles.Forsyth } 10337da2899SCharles.Forsyth 10437da2899SCharles.Forsyth (s, d) := buf.get(r.pos, count); 10537da2899SCharles.Forsyth r.pos = s + len d; 10637da2899SCharles.Forsyth 10737da2899SCharles.Forsyth if (d != nil) { 10837da2899SCharles.Forsyth rc <-= (d, nil); 10937da2899SCharles.Forsyth } else { 11037da2899SCharles.Forsyth if (r.rq == nil) 11137da2899SCharles.Forsyth waitlist = r :: waitlist; 11237da2899SCharles.Forsyth r.rq = (count, rc) :: r.rq; 11337da2899SCharles.Forsyth } 11437da2899SCharles.Forsyth 1153f1f06c5SCharles.Forsyth (nil, data, nil, wc) := <-fio.write => 11637da2899SCharles.Forsyth if (wc == nil) 11737da2899SCharles.Forsyth continue; 11837da2899SCharles.Forsyth if ((n := buf.put(data)) < len data) 11937da2899SCharles.Forsyth wc <-= (n, "write too long for buffer"); 12037da2899SCharles.Forsyth else 12137da2899SCharles.Forsyth wc <-= (n, nil); 12237da2899SCharles.Forsyth 12337da2899SCharles.Forsyth wl := waitlist; 12437da2899SCharles.Forsyth for (waitlist = nil; wl != nil; wl = tl wl) { 12537da2899SCharles.Forsyth r := hd wl; 12637da2899SCharles.Forsyth if (availcount == 0) { 12737da2899SCharles.Forsyth spawn worker(workchan, availchan); 12837da2899SCharles.Forsyth availcount++; 12937da2899SCharles.Forsyth } 13037da2899SCharles.Forsyth (count, rc) := hd r.rq; 13137da2899SCharles.Forsyth r.rq = tl r.rq; 13237da2899SCharles.Forsyth 13337da2899SCharles.Forsyth # optimisation: if the read request wants exactly the data provided 13437da2899SCharles.Forsyth # in the write request, then use the original data buffer. 13537da2899SCharles.Forsyth s: int; 13637da2899SCharles.Forsyth d: array of byte; 13737da2899SCharles.Forsyth if (count >= n && r.pos == buf.start + len buf.data - n) 13837da2899SCharles.Forsyth (s, d) = (r.pos, data); 13937da2899SCharles.Forsyth else 14037da2899SCharles.Forsyth (s, d) = buf.get(r.pos, count); 14137da2899SCharles.Forsyth r.pos = s + len d; 14237da2899SCharles.Forsyth workchan <-= (rc, d); 14337da2899SCharles.Forsyth availcount--; 14437da2899SCharles.Forsyth if (r.rq != nil) 14537da2899SCharles.Forsyth waitlist = r :: waitlist; 14637da2899SCharles.Forsyth d = nil; 14737da2899SCharles.Forsyth } 14837da2899SCharles.Forsyth data = nil; 14937da2899SCharles.Forsyth wl = nil; 15037da2899SCharles.Forsyth } 15137da2899SCharles.Forsyth} 15237da2899SCharles.Forsyth 15337da2899SCharles.Forsythworker(work: chan of (Sys->Rread, array of byte), ready: chan of int) 15437da2899SCharles.Forsyth{ 15537da2899SCharles.Forsyth for (;;) { 15637da2899SCharles.Forsyth (rc, data) := <-work; # blocks forever if the reading process is killed 15737da2899SCharles.Forsyth rc <-= (data, nil); 15837da2899SCharles.Forsyth (rc, data) = (nil, nil); 15937da2899SCharles.Forsyth ready <-= 1; 16037da2899SCharles.Forsyth } 16137da2899SCharles.Forsyth} 16237da2899SCharles.Forsyth 16337da2899SCharles.ForsythCircbuf.new(size: int): ref Circbuf 16437da2899SCharles.Forsyth{ 16537da2899SCharles.Forsyth return ref Circbuf(0, array[size] of byte); 16637da2899SCharles.Forsyth} 16737da2899SCharles.Forsyth 16837da2899SCharles.Forsyth# return number of bytes actually written 16937da2899SCharles.ForsythCircbuf.put(b: self ref Circbuf, d: array of byte): int 17037da2899SCharles.Forsyth{ 17137da2899SCharles.Forsyth blen := len b.data; 17237da2899SCharles.Forsyth # if too big to fit in buffer, truncate the write. 17337da2899SCharles.Forsyth if (len d > blen) 17437da2899SCharles.Forsyth d = d[0:blen]; 17537da2899SCharles.Forsyth dlen := len d; 17637da2899SCharles.Forsyth 17737da2899SCharles.Forsyth offset := b.start % blen; 17837da2899SCharles.Forsyth if (offset + dlen <= blen) { 17937da2899SCharles.Forsyth b.data[offset:] = d; 18037da2899SCharles.Forsyth } else { 18137da2899SCharles.Forsyth b.data[offset:] = d[0:blen - offset]; 18237da2899SCharles.Forsyth b.data[0:] = d[blen - offset:]; 18337da2899SCharles.Forsyth } 18437da2899SCharles.Forsyth b.start += dlen; 18537da2899SCharles.Forsyth return dlen; 18637da2899SCharles.Forsyth} 18737da2899SCharles.Forsyth 18837da2899SCharles.Forsyth# return (start, data) 18937da2899SCharles.ForsythCircbuf.get(b: self ref Circbuf, s, n: int): (int, array of byte) 19037da2899SCharles.Forsyth{ 19137da2899SCharles.Forsyth # if the beginning's been overrun, start from the earliest place we can. 19237da2899SCharles.Forsyth # we could put some indication of elided bytes in the buffer. 19337da2899SCharles.Forsyth if (s < b.start) 19437da2899SCharles.Forsyth s = b.start; 19537da2899SCharles.Forsyth blen := len b.data; 19637da2899SCharles.Forsyth if (s + n > b.start + blen) 19737da2899SCharles.Forsyth n = b.start + blen - s; 19837da2899SCharles.Forsyth if (n <= 0) 19937da2899SCharles.Forsyth return (s, nil); 20037da2899SCharles.Forsyth o := s % blen; 20137da2899SCharles.Forsyth d := array[n] of byte; 20237da2899SCharles.Forsyth if (o + n <= blen) 20337da2899SCharles.Forsyth d[0:] = b.data[o:o+n]; 20437da2899SCharles.Forsyth else { 20537da2899SCharles.Forsyth d[0:] = b.data[o:]; 20637da2899SCharles.Forsyth d[blen - o:] = b.data[0:o+n-blen]; 20737da2899SCharles.Forsyth } 20837da2899SCharles.Forsyth return (s, d); 20937da2899SCharles.Forsyth} 21037da2899SCharles.Forsyth 21137da2899SCharles.ForsythFIDHASHSIZE: con 32; 21237da2899SCharles.Forsyth 21337da2899SCharles.ForsythFidhash.new(): ref Fidhash 21437da2899SCharles.Forsyth{ 21537da2899SCharles.Forsyth return ref Fidhash(array[FIDHASHSIZE] of list of ref Fidrec); 21637da2899SCharles.Forsyth} 21737da2899SCharles.Forsyth 21837da2899SCharles.Forsyth# put an entry in the hash table. 21937da2899SCharles.Forsyth# assumes there is no current entry for the fid. 22037da2899SCharles.ForsythFidhash.put(ht: self ref Fidhash, f: ref Fidrec) 22137da2899SCharles.Forsyth{ 22237da2899SCharles.Forsyth slot := f.fid & (FIDHASHSIZE-1); 22337da2899SCharles.Forsyth ht.table[slot] = f :: ht.table[slot]; 22437da2899SCharles.Forsyth} 22537da2899SCharles.Forsyth 22637da2899SCharles.ForsythFidhash.get(ht: self ref Fidhash, fid: int): ref Fidrec 22737da2899SCharles.Forsyth{ 22837da2899SCharles.Forsyth for (l := ht.table[fid & (FIDHASHSIZE-1)]; l != nil; l = tl l) 22937da2899SCharles.Forsyth if ((hd l).fid == fid) 23037da2899SCharles.Forsyth return hd l; 23137da2899SCharles.Forsyth return nil; 23237da2899SCharles.Forsyth} 23337da2899SCharles.Forsyth 23437da2899SCharles.ForsythFidhash.del(ht: self ref Fidhash, f: ref Fidrec) 23537da2899SCharles.Forsyth{ 23637da2899SCharles.Forsyth slot := f.fid & (FIDHASHSIZE-1); 23737da2899SCharles.Forsyth nl: list of ref Fidrec; 23837da2899SCharles.Forsyth for (l := ht.table[slot]; l != nil; l = tl l) 23937da2899SCharles.Forsyth if ((hd l).fid != f.fid) 24037da2899SCharles.Forsyth nl = (hd l) :: nl; 24137da2899SCharles.Forsyth ht.table[slot] = nl; 24237da2899SCharles.Forsyth} 24337da2899SCharles.Forsyth 24437da2899SCharles.Forsythpathsplit(p: string): (string, string) 24537da2899SCharles.Forsyth{ 24637da2899SCharles.Forsyth for (i := len p - 1; i >= 0; i--) 24737da2899SCharles.Forsyth if (p[i] != '/') 24837da2899SCharles.Forsyth break; 24937da2899SCharles.Forsyth if (i < 0) 25037da2899SCharles.Forsyth return (p, nil); 25137da2899SCharles.Forsyth p = p[0:i+1]; 25237da2899SCharles.Forsyth for (i = len p - 1; i >=0; i--) 25337da2899SCharles.Forsyth if (p[i] == '/') 25437da2899SCharles.Forsyth break; 25537da2899SCharles.Forsyth if (i < 0) 25637da2899SCharles.Forsyth return (".", p); 25737da2899SCharles.Forsyth return (p[0:i+1], p[i+1:]); 25837da2899SCharles.Forsyth} 25937da2899SCharles.Forsyth 260