ref: f0bb0451afe4b3d4719c6b2446dfa8034c9eb4fc
parent: a848106feaaba815d72a871bf412b3fcd65338c0
author: Jacob Moody <[email protected]>
date: Thu Sep 12 11:09:11 EDT 2019
Refactor dec to use more channels Plug dec proc leak Move to next song on eof
--- a/dat.h
+++ b/dat.h
@@ -2,17 +2,7 @@
START,
STOP,
PAUSE,
-};
-
-/*
-* Dec represents a decoder process.
-* ctl is the control channel for pausing, starting, and stoping the proc.
-*/
-typedef struct Dec Dec;
-struct Dec{
- int decpid;
- int ctlpid;
- Channel *ctl;
+ NEXT,
};
/*
--- a/dec.c
+++ b/dec.c
@@ -16,7 +16,7 @@
typedef struct {
int inpipe;
Channel *ctl;
-} Ctlarg;
+} Writearg;
void
decodeproc(void *arg)
@@ -28,11 +28,13 @@
dup(nfd, 0);
dup(nfd, 2);
dup(a->outpipe, 1);
+ close(nfd);
+ close(a->outpipe);
procexecl(a->cpid, "/bin/play", "play", "-o", "/fd/1", a->file, nil);
}
void
-ctlproc(void *arg)
+writethread(void *arg)
{
int afd;
int bufsize;
@@ -40,14 +42,13 @@
enum decmsg msg;
char *buf;
- Ctlarg *a = arg;
+ Writearg *a = arg;
Channel *c = a->ctl;
int inpipe = a->inpipe;
- free(a);
afd = open("/dev/audio", OWRITE);
if(afd < 0)
- threadexits("could not open audio device");
+ sysfatal("could not open audio device");
bufsize = iounit(afd);
buf = emalloc(bufsize);
@@ -55,10 +56,12 @@
if(nbrecv(c, &msg) != 0)
switch(msg){
case STOP:
+ free(buf);
+ close(afd);
threadexits(nil);
break;
case PAUSE:
- //Block until we get a START message
+ /* Block until we get a START message */
while(msg != START)
recv(c, &msg);
break;
@@ -65,35 +68,118 @@
}
n = read(inpipe, buf, bufsize);
write(afd, buf, n);
+ yield();
}
}
+enum{
+ QUEUE,
+ CTL,
+ WAIT,
+};
+
void
-playfile(Dec *d, char *file)
+ctlproc(void *arg)
{
- Decodearg *a;
- Ctlarg *c;
+ Channel **chans = arg;
+ Channel *q = chans[0];
+ Channel *c = chans[1];
+ Channel *pop = chans[2];
+ Channel *w = threadwaitchan();
+ free(chans);
+
+ char *path;
+ enum decmsg msg;
+ Waitmsg *wmsg;
+
+ Decodearg a;
+ Writearg wr;
int p[2];
+ /* This allows for the main thread to kill the decoder on exit */
+ extern int decpid = -1;
- if(d->ctl != nil)
- chanfree(d->ctl);
-
+ Alt alts[] = {
+ {q, &path, CHANRCV},
+ {c, &msg, CHANRCV},
+ {w, &wmsg, CHANRCV},
+ {nil, nil, CHANEND},
+ };
+
pipe(p);
- a = emalloc(sizeof(Decodearg));
- a->cpid = chancreate(sizeof(int), 0);
- a->outpipe = p[1];
- a->file = file;
- procrfork(decodeproc, a, 8192, RFFDG);
- recv(a->cpid, &(d->decpid));
- chanfree(a->cpid);
- free(a);
- c = emalloc(sizeof(Ctlarg));
- c->ctl = d->ctl = chancreate(sizeof(enum decmsg), 0);
- c->inpipe = p[0];
- d->ctlpid = procrfork(ctlproc, c, 8192, RFFDG);
- /* Other proc frees c */
+ a.cpid = chancreate(sizeof(int), 0);
+ a.outpipe = p[0];
+ wr.ctl = chancreate(sizeof(enum decmsg), 0);
+ wr.inpipe = p[1];
+
+ /* Start first song to stop blocks on writethread read */
+ a.file = recvp(q);
+ procrfork(decodeproc, &a, 8192, RFFDG);
+ recv(a.cpid, &decpid);
+ threadcreate(writethread, &wr, 8192);
+
+ for(;;){
+ switch(alt(alts)){
+ case WAIT:
+ if(strstr(wmsg->msg, "eof")){
+ decpid = -1;
+ send(pop, nil);
+ }
+ free(wmsg);
+ break;
+ case CTL:
+ if(msg == NEXT){
+ killgrp(decpid);
+ decpid = -1;
+ send(pop, nil);
+ }else
+ send(wr.ctl, &msg);
+ break;
+ case QUEUE:
+ a.file = path;
+ if(decpid != -1)
+ killgrp(decpid);
+ procrfork(decodeproc, &a, 8192, RFFDG);
+ recv(a.cpid, &decpid);
+ break;
+ default:
+ goto cleanup;
+ }
+ }
+
+
+cleanup:
+ if(decpid != -1)
+ killgrp(decpid);
+ chanfree(wr.ctl);
+ chanfree(a.cpid);
close(p[0]);
close(p[1]);
+
}
+
+/*
+* Spawns the decoder processes.
+* q is a queue for next songs. chan char*
+* c is for sending control messages. chan enum decmsg
+* nil msg is sent over pop on song change.
+*/
+void
+spawndec(Channel **q, Channel **c, Channel **pop)
+{
+ Channel **chans = emalloc(sizeof(Channel*) * 3);
+
+ if(*q == nil)
+ *q = chancreate(sizeof(char*), 0);
+ if(*c == nil)
+ *c = chancreate(sizeof(enum decmsg), 0);
+ if(*pop == nil)
+ *pop = chancreate(sizeof(char), 0);
+
+ chans[0] = *q;
+ chans[1] = *c;
+ chans[2] = *pop;
+
+ procrfork(ctlproc, chans, 8192, RFFDG);
+}
\ No newline at end of file
--- a/fncs.h
+++ b/fncs.h
@@ -1,5 +1,5 @@
/* dec.c */
-void playfile(Dec*,char*);
+void spawndec(Channel**,Channel**,Channel**);
/* mpl.c */
void quit(char*);
@@ -9,6 +9,7 @@
u64int bebtoi(uchar*,int);
u64int lebtoi(uchar*,int);
void kill(int);
+void killgrp(int);
/* id3.c */
ID3v1* readid3(int);
--- a/mpl.c
+++ b/mpl.c
@@ -13,6 +13,7 @@
MOUSEC,
RESIZEC,
KEYC,
+ QUEUEPOP,
NONE
};
@@ -19,14 +20,19 @@
Mousectl *mctl;
Keyboardctl *kctl;
+Channel *queuein;
+Channel *queueout;
+Channel *ctl;
Album *a;
-Dec d;
-int cursong;
+int cursong;
+int decpid;
Image *black;
Image *red;
Image *background;
+enum decmsg msg;
+
void
quit(char *err)
{
@@ -47,25 +53,21 @@
flushimage(display, Refnone);
}
-void
-play(void)
+char*
+nextsong(void)
{
- enum decmsg msg = STOP;
- send(d.ctl, &msg);
- kill(d.decpid);
cursong = cursong < 0 ? a->nsong-1 : cursong;
cursong = cursong > a->nsong-1 ? 0 : cursong;
- playfile(&d, a->songs[cursong]->path);
- eresized(0);
+ return a->songs[cursong]->path;
}
void
handleaction(Rune kbd)
{
- enum decmsg msg;
switch(kbd){
case Kbs:
case Kdel:
+ killgrp(decpid);
quit(nil);
break;
case 'w':
@@ -73,19 +75,23 @@
break;
case 'p':
msg = PAUSE;
- send(d.ctl, &msg);
+ send(ctl, &msg);
break;
case 'l':
msg = START;
- send(d.ctl, &msg);
+ send(ctl, &msg);
break;
case 'n':
cursong++;
- play();
+ nextsong();
+ sendp(queuein, nextsong());
+ eresized(0);
break;
case 'm':
cursong--;
- play();
+ nextsong();
+ sendp(queuein, nextsong());
+ eresized(0);
break;
}
}
@@ -104,6 +110,7 @@
Rune kbd;
int resize[2];
cursong = 0;
+ queuein = queueout = ctl = nil;
//TODO: Use ARGBEGIN
argv0 = argv[0];
@@ -118,7 +125,6 @@
if((kctl = initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
- memset(&d, 0, sizeof d);
red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlue);
black = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, DBlack);
background = allocimagemix(display, DPaleyellow, DPalegreen);
@@ -128,7 +134,8 @@
quit("nil album");
if(a->nsong == 0)
quit("no songs");
- playfile(&d, a->songs[0]->path);
+ spawndec(&queuein, &ctl, &queueout);
+ send(queuein, &(a->songs[0]->path));
handleaction('w');
Alt alts[] = {
@@ -135,6 +142,7 @@
{mctl->c, &mouse, CHANRCV},
{mctl->resizec, resize, CHANRCV},
{kctl->c, &kbd, CHANRCV},
+ {queueout, nil, CHANRCV},
{nil, nil, CHANEND},
};
@@ -145,6 +153,9 @@
break;
case RESIZEC:
eresized(1);
+ break;
+ case QUEUEPOP:
+ handleaction(L'n');
break;
}
}
--- a/util.c
+++ b/util.c
@@ -49,10 +49,24 @@
int nfd;
char *note = smprint( "/proc/%d/note", pid);
nfd = open(note, OWRITE);
- //If the file does not exist, it is probably already dead
if(nfd<0)
- return;
- write(nfd, "kill", 4);
+ sysfatal("proc doesn't exist");
+ if(write(nfd, "kill", 4)!=4)
+ sysfatal("could not write to note");
close(nfd);
free(note);
}
+
+void
+killgrp(int pid)
+{
+ int nfd;
+ char *note = smprint( "/proc/%d/notepg", pid);
+ nfd = open(note, OWRITE);
+ if(nfd<0)
+ sysfatal("proc doesn't exist");
+ if(write(nfd, "kill", 4)!=4)
+ sysfatal("could not write to note");
+ close(nfd);
+ free(note);
+}
\ No newline at end of file