mirror of
https://github.com/3proxy/3proxy.git
synced 2026-10-28 22:05:48 +08:00
Event loop: what is due called when waiting fails; units on Windows
looponce() called nothing due when waiting for the sockets failed, returning at once: a caller waiting for a timer turned forever. What is due is called all the same, and on Windows WSAEINTR is no failure, not errno. looptest reports any failure to wait with its error; fibertest starts Winsock, its loop could not be made without, and recurses into half of a fiber's stack rather than most of it, which Windows takes more of. The units case shows what a test which never finished wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4e1455b419
commit
30f6043160
10
src/loop.c
10
src/loop.c
@ -660,7 +660,13 @@ int looponce(struct loop *l, int maxwait){
|
|||||||
if(l->pfds[j].revents) readyadd(l, l->pios[j], l->pfds[j].revents);
|
if(l->pfds[j].revents) readyadd(l, l->pios[j], l->pfds[j].revents);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if(n < 0 && errno != EINTR) return -1;
|
/* failing, what is due is still called: the caller decides whether to
|
||||||
|
go on */
|
||||||
|
#ifdef _WIN32
|
||||||
|
if(n < 0 && WSAGetLastError() == WSAEINTR) n = 0;
|
||||||
|
#else
|
||||||
|
if(n < 0 && errno == EINTR) n = 0;
|
||||||
|
#endif
|
||||||
l->now = l->clock();
|
l->now = l->clock();
|
||||||
if(woken){
|
if(woken){
|
||||||
wakedrain(l);
|
wakedrain(l);
|
||||||
@ -671,7 +677,7 @@ int looponce(struct loop *l, int maxwait){
|
|||||||
|
|
||||||
if(io) io->func(l, io, l->readyev[i]);
|
if(io) io->func(l, io, l->readyev[i]);
|
||||||
}
|
}
|
||||||
n = l->nready;
|
if(n >= 0) n = l->nready;
|
||||||
l->nready = 0;
|
l->nready = 0;
|
||||||
timersturn(l);
|
timersturn(l);
|
||||||
timersfire(l);
|
timersfire(l);
|
||||||
|
|||||||
@ -18,8 +18,10 @@ def run(t):
|
|||||||
try:
|
try:
|
||||||
done = subprocess.run([path, seed], stdout=subprocess.PIPE,
|
done = subprocess.run([path, seed], stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT, timeout=120)
|
stderr=subprocess.STDOUT, timeout=120)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired as exc:
|
||||||
t.fail(f"{unit} {seed} finishes")
|
out = (exc.output or b"").decode(errors="replace")
|
||||||
|
t.fail(f"{unit} {seed} finishes",
|
||||||
|
actual="last: " + (out.splitlines() or ["nothing"])[-1])
|
||||||
continue
|
continue
|
||||||
out = done.stdout.decode(errors="replace")
|
out = done.stdout.decode(errors="replace")
|
||||||
for line in out.splitlines():
|
for line in out.splitlines():
|
||||||
|
|||||||
@ -234,7 +234,7 @@ static unsigned deepresult;
|
|||||||
|
|
||||||
static void deeper(void *arg){
|
static void deeper(void *arg){
|
||||||
fiberyield();
|
fiberyield();
|
||||||
deepresult = deep(150);
|
deepresult = deep(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void depth(void){
|
static void depth(void){
|
||||||
@ -243,7 +243,7 @@ static void depth(void){
|
|||||||
if(fiberinit(&f, STACK)) return;
|
if(fiberinit(&f, STACK)) return;
|
||||||
fiberstart(&f, deeper, NULL);
|
fiberstart(&f, deeper, NULL);
|
||||||
while(fiberresume(&f));
|
while(fiberresume(&f));
|
||||||
check(deepresult == deep(150), "a fiber uses most of its stack", "wrong result");
|
check(deepresult == deep(100), "a fiber uses half of its stack", "wrong result");
|
||||||
fiberfree(&f);
|
fiberfree(&f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,7 +283,10 @@ static void onloop(void){
|
|||||||
int i, left, early = 0;
|
int i, left, early = 0;
|
||||||
char what[64];
|
char what[64];
|
||||||
|
|
||||||
if(loopinit(&l)) return;
|
if(loopinit(&l)){
|
||||||
|
check(0, "fibers sleep on the loop's timers", "loopinit() failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
for(i = 0; i < NWAIT; i++){
|
for(i = 0; i < NWAIT; i++){
|
||||||
memset(w + i, 0, sizeof(w[i]));
|
memset(w + i, 0, sizeof(w[i]));
|
||||||
if(fiberinit(&w[i].f, STACK)) return;
|
if(fiberinit(&w[i].f, STACK)) return;
|
||||||
@ -306,6 +309,11 @@ static void onloop(void){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv){
|
int main(int argc, char **argv){
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wd;
|
||||||
|
|
||||||
|
WSAStartup(MAKEWORD(2, 2), &wd);
|
||||||
|
#endif
|
||||||
if(!fiberusable()){
|
if(!fiberusable()){
|
||||||
printf("ok fibers cannot be used here (a shadow stack): each client has a thread\n");
|
printf("ok fibers cannot be used here (a shadow stack): each client has a thread\n");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,22 @@
|
|||||||
#include "../src/proxy.h"
|
#include "../src/proxy.h"
|
||||||
#include "../src/loop.h"
|
#include "../src/loop.h"
|
||||||
|
|
||||||
static int failures;
|
static int failures, waitfails, lasterr;
|
||||||
|
|
||||||
|
/* looponce(), a failure to wait counted, with its error */
|
||||||
|
static int turn(struct loop *l, int maxwait){
|
||||||
|
int n = looponce(l, maxwait);
|
||||||
|
|
||||||
|
if(n < 0){
|
||||||
|
waitfails++;
|
||||||
|
#ifdef _WIN32
|
||||||
|
lasterr = WSAGetLastError();
|
||||||
|
#else
|
||||||
|
lasterr = errno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
static void check(int cond, const char *label, const char *what){
|
static void check(int cond, const char *label, const char *what){
|
||||||
if(cond) printf("ok %s\n", label);
|
if(cond) printf("ok %s\n", label);
|
||||||
@ -134,7 +149,7 @@ static void timertest(void){
|
|||||||
/* steps of a few ms to a second, and now and then a long stall */
|
/* steps of a few ms to a second, and now and then a long stall */
|
||||||
if(rand_upto(500) == 0) fakenow += 1 + rand_upto(300000);
|
if(rand_upto(500) == 0) fakenow += 1 + rand_upto(300000);
|
||||||
else fakenow += 1 + rand_upto(rand_upto(4) ? 1500 : 20);
|
else fakenow += 1 + rand_upto(rand_upto(4) ? 1500 : 20);
|
||||||
looponce(&l, 0);
|
turn(&l, 0);
|
||||||
steps++;
|
steps++;
|
||||||
}
|
}
|
||||||
pending = 0;
|
pending = 0;
|
||||||
@ -231,25 +246,25 @@ static void iotest(void){
|
|||||||
a.io.func = iocall;
|
a.io.func = iocall;
|
||||||
check(!loopadd(&l, &a.io), "sockets: a socket is added", "loopadd() failed");
|
check(!loopadd(&l, &a.io), "sockets: a socket is added", "loopadd() failed");
|
||||||
|
|
||||||
looponce(&l, 100);
|
turn(&l, 100);
|
||||||
check(a.calls == 0, "sockets: nothing to read, no call", "called");
|
check(a.calls == 0, "sockets: nothing to read, no call", "called");
|
||||||
|
|
||||||
send(s2, "x", 1, 0);
|
send(s2, "x", 1, 0);
|
||||||
for(i = 0; i < 10 && !a.calls; i++) looponce(&l, 200);
|
for(i = 0; i < 10 && !a.calls; i++) turn(&l, 200);
|
||||||
check(a.calls >= 1 && (a.revents & POLLIN), "sockets: what comes is readable", "no POLLIN");
|
check(a.calls >= 1 && (a.revents & POLLIN), "sockets: what comes is readable", "no POLLIN");
|
||||||
recv(s1, buf, sizeof(buf), 0);
|
recv(s1, buf, sizeof(buf), 0);
|
||||||
|
|
||||||
a.calls = 0;
|
a.calls = 0;
|
||||||
a.revents = 0;
|
a.revents = 0;
|
||||||
loopmod(&l, &a.io, POLLOUT);
|
loopmod(&l, &a.io, POLLOUT);
|
||||||
for(i = 0; i < 10 && !a.calls; i++) looponce(&l, 200);
|
for(i = 0; i < 10 && !a.calls; i++) turn(&l, 200);
|
||||||
check(a.calls >= 1 && (a.revents & POLLOUT) && !(a.revents & POLLIN),
|
check(a.calls >= 1 && (a.revents & POLLOUT) && !(a.revents & POLLIN),
|
||||||
"sockets: waited for writing, writable", "no POLLOUT");
|
"sockets: waited for writing, writable", "no POLLOUT");
|
||||||
|
|
||||||
loopmod(&l, &a.io, 0);
|
loopmod(&l, &a.io, 0);
|
||||||
a.calls = 0;
|
a.calls = 0;
|
||||||
send(s2, "y", 1, 0);
|
send(s2, "y", 1, 0);
|
||||||
looponce(&l, 200);
|
turn(&l, 200);
|
||||||
check(a.calls == 0, "sockets: waited for nothing, not called", "called");
|
check(a.calls == 0, "sockets: waited for nothing, not called", "called");
|
||||||
|
|
||||||
/* both ready: whichever is called first deletes the other */
|
/* both ready: whichever is called first deletes the other */
|
||||||
@ -262,7 +277,7 @@ static void iotest(void){
|
|||||||
c.other = &a;
|
c.other = &a;
|
||||||
send(s4, "z", 1, 0);
|
send(s4, "z", 1, 0);
|
||||||
a.calls = c.calls = 0;
|
a.calls = c.calls = 0;
|
||||||
for(i = 0; i < 10 && a.calls + c.calls == 0; i++) looponce(&l, 200);
|
for(i = 0; i < 10 && a.calls + c.calls == 0; i++) turn(&l, 200);
|
||||||
check(a.calls + c.calls == 1, "sockets: one deleted in this turn is not called", "both called");
|
check(a.calls + c.calls == 1, "sockets: one deleted in this turn is not called", "both called");
|
||||||
|
|
||||||
/* the one deleted can be added again; the far side closing it is
|
/* the one deleted can be added again; the far side closing it is
|
||||||
@ -275,7 +290,7 @@ static void iotest(void){
|
|||||||
a.calls = 0;
|
a.calls = 0;
|
||||||
a.revents = 0;
|
a.revents = 0;
|
||||||
closesock(s2);
|
closesock(s2);
|
||||||
for(i = 0; i < 10 && !a.calls; i++) looponce(&l, 200);
|
for(i = 0; i < 10 && !a.calls; i++) turn(&l, 200);
|
||||||
check(a.calls >= 1 && (a.revents & (POLLIN | POLLHUP)) && recv(s1, buf, sizeof(buf), 0) == 0,
|
check(a.calls >= 1 && (a.revents & (POLLIN | POLLHUP)) && recv(s1, buf, sizeof(buf), 0) == 0,
|
||||||
"sockets: the far side closing is readable", "not reported");
|
"sockets: the far side closing is readable", "not reported");
|
||||||
|
|
||||||
@ -349,7 +364,7 @@ static void realtest(void){
|
|||||||
t.func = realfire;
|
t.func = realfire;
|
||||||
start = looptime();
|
start = looptime();
|
||||||
looptimerset(&tl, &t, start + 150);
|
looptimerset(&tl, &t, start + 150);
|
||||||
while(!timerfired) looponce(&tl, -1);
|
while(!timerfired) turn(&tl, -1);
|
||||||
took = looptime() - start;
|
took = looptime() - start;
|
||||||
sprintf(what, "fired after %u ms", (unsigned)took);
|
sprintf(what, "fired after %u ms", (unsigned)took);
|
||||||
check(took >= 150 && took < 1100, "real time: a timer fires when it is due", what);
|
check(took >= 150 && took < 1100, "real time: a timer fires when it is due", what);
|
||||||
@ -357,7 +372,7 @@ static void realtest(void){
|
|||||||
start = looptime();
|
start = looptime();
|
||||||
if(startposter()) check(0, "real time: a thread is started", "failed");
|
if(startposter()) check(0, "real time: a thread is started", "failed");
|
||||||
else {
|
else {
|
||||||
looponce(&tl, 5000);
|
turn(&tl, 5000);
|
||||||
took = looptime() - start;
|
took = looptime() - start;
|
||||||
sprintf(what, "after %u ms", (unsigned)took);
|
sprintf(what, "after %u ms", (unsigned)took);
|
||||||
check(posted && took < 2000, "real time: a message from another thread wakes the loop", what);
|
check(posted && took < 2000, "real time: a message from another thread wakes the loop", what);
|
||||||
@ -386,5 +401,11 @@ int main(int argc, char **argv){
|
|||||||
timertest();
|
timertest();
|
||||||
iotest();
|
iotest();
|
||||||
realtest();
|
realtest();
|
||||||
|
{
|
||||||
|
char what[64];
|
||||||
|
|
||||||
|
sprintf(what, "%d failed, the last with error %d", waitfails, lasterr);
|
||||||
|
check(!waitfails, "waiting for sockets never fails", what);
|
||||||
|
}
|
||||||
return failures;
|
return failures;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user