rio: fix deadlock

we can't really change the Window *input from
outside the winctl() thread. the problem is
that the window might end up reading the
mouse (scroll, select) which makes the w->cctl
channel block once you try to talk to the
window again (from the mousethread). this also
means we have to coordinate window switchin
from the winctl proc waiting for the current
window to release the input and then take over.
thers a new Winctl message Topped that basically
does that now using Wakeup and a chan to
synchronize.
This commit is contained in:
cinap_lenrek 2012-10-22 07:03:47 +02:00
parent 54d2424a7c
commit 99216e0129
5 changed files with 71 additions and 47 deletions

View file

@ -61,13 +61,14 @@ enum /* control messages */
Wakeup,
Reshaped,
Moved,
Topped,
Repaint,
Refresh,
Movemouse,
Rawon,
Rawoff,
Holdon,
Holdoff,
Repaint,
Deleted,
Exited,
};
@ -76,7 +77,7 @@ struct Wctlmesg
{
int type;
Rectangle r;
Image *image;
void *p;
};
struct Conswritemesg
@ -132,7 +133,7 @@ struct Window
Mousectl mc;
Mouseinfo mouse;
Channel *ck; /* chan(char*) */
Channel *cctl; /* chan(Wctlmesg)[20] */
Channel *cctl; /* chan(Wctlmesg)[4] */
Channel *conswrite; /* chan(Conswritemesg) */
Channel *consread; /* chan(Consreadmesg) */
Channel *mouseread; /* chan(Mousereadmesg) */
@ -188,8 +189,7 @@ char* wcontents(Window*, int*);
int wbswidth(Window*, Rune);
int wclickmatch(Window*, int, int, int, uint*);
int wclose(Window*);
int wctlmesg(Window*, int, Rectangle, Image*);
int wctlmesg(Window*, int, Rectangle, Image*);
int wctlmesg(Window*, int, Rectangle, void*);
uint wbacknl(Window*, uint, uint);
uint winsert(Window*, Rune*, int, uint);
void waddraw(Window*, Rune*, int);
@ -213,7 +213,7 @@ void wresize(Window*, Image*, int);
void wscrdraw(Window*);
void wscroll(Window*, int);
void wselect(Window*);
void wsendctlmesg(Window*, int, Rectangle, Image*);
void wsendctlmesg(Window*, int, Rectangle, void*);
void wsetcursor(Window*, int);
void wsetname(Window*);
void wsetorigin(Window*, uint, int);

View file

@ -345,7 +345,7 @@ keyboardthread(void*)
while(s = recvp(kbdchan)){
if(*s == 'k' || *s == 'K')
shiftdown = utfrune(s+1, Kshift) != nil;
if(input == nil || input->deleted || sendp(input->ck, s) <= 0)
if(input == nil || sendp(input->ck, s) <= 0)
free(s);
}
}
@ -1113,10 +1113,8 @@ resize(void)
return;
incref(w);
i = sweep();
if(i){
if(i)
wsendctlmesg(w, Reshaped, i->r, i);
wcurrent(w);
}
wclose(w);
}
@ -1132,10 +1130,8 @@ move(void)
return;
incref(w);
i = drag(w, &r);
if(i){
if(i)
wsendctlmesg(w, Moved, r, i);
wcurrent(w);
}
cornercursor(w, mouse->xy, 1);
wclose(w);
}
@ -1154,8 +1150,6 @@ whide(Window *w)
incref(w);
i = allocimage(display, w->screenr, w->i->chan, 0, DNofill);
if(i){
if(w == input)
input = nil;
hidden[nhidden++] = w;
wsendctlmesg(w, Reshaped, ZR, i);
}
@ -1180,7 +1174,6 @@ wunhide(Window *w)
--nhidden;
memmove(hidden+j, hidden+j+1, (nhidden-j)*sizeof(Window*));
wsendctlmesg(w, Reshaped, w->i->r, i);
wcurrent(w);
}
wclose(w);
return i!=0;
@ -1257,7 +1250,6 @@ new(Image *i, int hideit, int scrollit, int pid, char *dir, char *cmd, char **ar
threadcreate(winctl, w, 8192);
if(!hideit)
wcurrent(w);
flushimage(display, 1);
if(pid == 0){
arg = emalloc(5*sizeof(void*));
arg[0] = w;

View file

@ -388,6 +388,7 @@ wctlcmd(Window *w, Rectangle r, int cmd, char *err)
wbottomme(w);
return 1;
case Current:
wtopme(w);
wcurrent(w);
return 1;
case Hide:

View file

@ -113,10 +113,7 @@ wresize(Window *w, Image *i, int move)
wsetselect(w, w->q0, w->q1);
wscrdraw(w);
}
if(w == input)
wborder(w, Selborder);
else
wborder(w, Unselborder);
wborder(w, Selborder);
wsetname(w);
w->topped = ++topped;
w->resized = TRUE;
@ -312,7 +309,7 @@ winctl(void *arg)
send(mrm.cm, &m.Mouse);
continue;
case WCtl:
if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Exited){
if(wctlmesg(w, wcm.type, wcm.r, wcm.p) == Exited){
for(i=0; i<nelem(kbdq); i++)
free(kbdq[i]);
chanfree(crm.c1);
@ -1058,26 +1055,29 @@ wselect(Window *w)
}
void
wsendctlmesg(Window *w, int type, Rectangle r, Image *image)
wsendctlmesg(Window *w, int type, Rectangle r, void *p)
{
Wctlmesg wcm;
wcm.type = type;
wcm.r = r;
wcm.image = image;
wcm.p = p;
send(w->cctl, &wcm);
}
int
wctlmesg(Window *w, int m, Rectangle r, Image *i)
wctlmesg(Window *w, int m, Rectangle r, void *p)
{
char buf[64];
Image *i = p;
switch(m){
default:
error("unknown control message");
break;
case Wakeup:
if(p!=nil)
sendp((Channel*)p, w);
break;
case Moved:
case Reshaped:
@ -1090,6 +1090,55 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
wresize(w, i, m==Moved);
proccreate(deletetimeoutproc, estrdup(buf), 4096);
flushimage(display, 1);
if(Dx(r)<=0){ /* window got hidden, if we had the input, drop it */
if(w==input)
input = nil;
break;
}
/* fall through to get input if needed */
case Topped:
if(w->deleted || w==input)
break;
if(input!=nil){
Window *oi;
Channel *c;
oi = input;
incref(oi);
/*
* have to wait until old input responds before
* changing input to us because the window might
* currently be mouse tracking and it is not
* prepared for getting its input revoked.
*/
c = chancreate(sizeof(void*), 0);
wsendctlmesg(oi, Wakeup, ZR, c);
recv(c, nil);
chanfree(c);
/*
* if we are still top window and nobody else has taken
* input from original window, take the input.
*/
if(!w->deleted && w->topped==topped && oi==input){
input = w;
oi->wctlready = 1;
wsendctlmesg(oi, Repaint, ZR, nil);
}
wclose(oi);
} else
input = w;
w->wctlready = 1;
if(m!=Topped && w==input)
break;
/* fall thrugh for redraw after input change */
case Repaint:
if(w->deleted || Dx(w->screenr)<=0)
break;
wrepaint(w);
flushimage(display, 1);
break;
case Refresh:
if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i->r) || w->mouseopen)
@ -1114,12 +1163,10 @@ wctlmesg(Window *w, int m, Rectangle r, Image *i)
break;
case Holdon:
case Holdoff:
if(w == input)
wsetcursor(w, 0);
/* no break */
case Repaint:
if(w->deleted)
break;
if(w==input)
wsetcursor(w, 0);
wrepaint(w);
flushimage(display, 1);
break;
@ -1206,22 +1253,8 @@ wpointto(Point pt)
void
wcurrent(Window *w)
{
Window *oi;
if(wkeyboard!=nil && w==wkeyboard)
return;
oi = input;
input = w;
if(w != oi){
if(oi){
oi->wctlready = 1;
wsendctlmesg(oi, Repaint, ZR, nil);
}
if(w){
w->wctlready = 1;
wsendctlmesg(w, Repaint, ZR, nil);
}
}
if(w!=nil && w!=input)
wsendctlmesg(w, Topped, ZR, nil);
}
void

View file

@ -190,7 +190,6 @@ xfidattach(Xfid *x)
if(pid == 0)
pid = -1; /* make sure we don't pop a shell! - UGH */
w = new(i, hideit, scrollit, pid, nil, nil, nil);
flushimage(display, 1);
newlymade = TRUE;
}else
err = Ewindow;
@ -550,7 +549,6 @@ xfidwrite(Xfid *x)
filsysrespond(x->fs, x, &fc, buf);
return;
}
flushimage(display, 1);
break;
default: