static int getlock(char *lock) { char buf[ERRMAX]; int i, fd; enum { SECS = 200 }; for(i=0; i<SECS*10; i++){ fd = wcreate(lock, ORDWR, DMEXCL|0666); if(fd >= 0) return fd; buf[0] = '\0'; rerrstr(buf, sizeof buf); if(strstr(buf, "locked") == nil) break; sleep(1000/10); } werrstr("couldn't acquire lock %s: %r", lock); return -1; }
/* insert word into a redblack tree */ int insert(char *word) { TREEREC *curr = ans->root, *par, *gpar, *prev = NULL, *wcreate(); int val; if( ans->root == NULL ) { ans->ans = ans->root = wcreate(word, NULL); return 1; } while( curr != NULL && (val = scmp(word, curr->word)) != 0 ) { prev = curr; if( val > 0 ) curr = curr->right; else curr = curr->left; } ans->ans = curr; if( curr == NULL ) /* insert a new node, rotate up if necessary */ { if( val > 0 ) curr = prev->right = wcreate(word, prev); else curr = prev->left = wcreate(word, prev); curr->colour = RED; while( (par = curr->par) != NULL && ( gpar = par->par ) != NULL && curr->par->colour == RED ) { if( par == gpar->left ) { if( gpar->right!=NULL && gpar->right->colour == RED ) { par->colour = BLACK; gpar->right->colour = BLACK; gpar->colour = RED; curr = gpar; } else { if( curr == par->right ) { curr = par; leftrotate(ans, curr); par = curr->par; } par->colour = BLACK; if( ( gpar=par->par ) != NULL ) { gpar->colour = RED; rightrotate(ans, gpar); } } } else { if( gpar->left!=NULL && gpar->left->colour == RED ) { par->colour = BLACK; gpar->left->colour = BLACK; gpar->colour = RED; curr = gpar; } else { if( curr == par->left ) { curr = par; rightrotate(ans, curr); par = curr->par; } par->colour = BLACK; if( ( gpar=par->par ) != NULL ) { gpar->colour = RED; leftrotate(ans, gpar); } } } } if( curr->par == NULL ) ans->root = curr; ans->root->colour = BLACK; return 1; } return 0; }
/* * Attempt to install a new page. If t==0 we are creating. * Otherwise, we are editing and t must be set to the current * version (t is the version we started with) to avoid conflicting * writes. * * If there is a conflicting write, we still write the page to * the history file, but mark it as a failed write. */ int writepage(int num, uint32_t t, String *s, char *title) { char tmp[40], tmplock[40], err[ERRMAX], hist[40], *p; int conflict, lfd, fd; Biobuf *b; String *os; sprint(tmp, "d/%d", num); sprint(tmplock, "d/L.%d", num); sprint(hist, "d/%d.hist", num); if((lfd = getlock(tmplock)) < 0) return -1; conflict = 0; if(b = wBopen(tmp, OREAD)){ Brdline(b, '\n'); /* title */ if(p = Brdline(b, '\n')) /* version */ p[Blinelen(b)-1] = '\0'; if(p==nil || p[0] != 'D'){ snprint(err, sizeof err, "bad format in extant file"); conflict = 1; }else if(strtoul(p+1, 0, 0) != t){ os = Brdstring(b); /* why read the whole file? */ p = strchr(s_to_c(s), '\n'); if(p!=nil && strcmp(p+1, s_to_c(os))==0){ /* ignore dup write */ close(lfd); s_free(os); Bterm(b); return 0; } s_free(os); snprint(err, sizeof err, "update conflict %lud != %s", t, p+1); conflict = 1; } Bterm(b); }else{ if(t != 0){ close(lfd); werrstr("did not expect to create"); return -1; } } if((fd = wopen(hist, OWRITE)) < 0){ if((fd = wcreate(hist, OWRITE, 0666)) < 0){ close(lfd); return -1; }else fprint(fd, "%s\n", title); } if(seek(fd, 0, 2) < 0 || (conflict && write(fd, "X\n", 2) != 2) || write(fd, s_to_c(s), s_len(s)) != s_len(s)){ close(fd); close(lfd); return -1; } close(fd); if(conflict){ close(lfd); voidcache(num); werrstr(err); return -1; } if((fd = wcreate(tmp, OWRITE, 0666)) < 0){ close(lfd); voidcache(num); return -1; } if(write(fd, title, strlen(title)) != strlen(title) || write(fd, "\n", 1) != 1 || write(fd, s_to_c(s), s_len(s)) != s_len(s)){ close(fd); close(lfd); voidcache(num); return -1; } close(fd); close(lfd); voidcache(num); return 0; }