From f718cec407405b364332193b0049d8cf747e7b63 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 1 Mar 2018 22:31:02 -0800 Subject: [PATCH] Rune - Threading fixes, cleanups * Implement importdesc(). This sets O_NONBLOCK on the descriptor when extpread()/extpwrite() are not available. * Cleanup the Stream and StreamPair class a bit. Separate the allocation for the read and write streams in StreamPair. This allows accessors to separately thread the read and write side without deadlocking each other. * Fix per-rgd TaskReadAry and TaskWriteAry handling. Recent threading changes now allow all rgd's to maintain their own KQueue. * Preallocate the array of lists instead of having an array of pointers for TaskReadAry and TaskWriteAry. Its just easier, and the size of the array is dynamic anyway (based on number of descriptors). * Implement I/O support on systems which do not have extpread() and extpwrite(). * Fix RunThreads accounting. * Fix threadPushRunnable() use cases. * Remove a bunch of LASTQUEUE related debugging. --- classes/stdio/file.d | 3 ++ classes/sys/fd.d | 2 ++ classes/sys/stream.d | 28 ++++++++++++----- libgen/syscalls.c | 6 ++++ libruntime/event.c | 50 ++++++++++++++--------------- libruntime/sys_fd.c | 67 ++++++++++++++++++++++++++++----------- libruntime/sys_thread.c | 1 + libruntime/syscalls.h | 1 + libruntime/thread.c | 69 +++++++++++++---------------------------- libruntime/thread.h | 8 +++-- tests/pipe1.d | 21 ++++++++----- tests/threadov.d | 10 ++++-- tests/ttest.c | 1 + 13 files changed, 155 insertions(+), 112 deletions(-) diff --git a/classes/stdio/file.d b/classes/stdio/file.d index bb0d424..20a6d36 100644 --- a/classes/stdio/file.d +++ b/classes/stdio/file.d @@ -31,18 +31,21 @@ modinit() { stdin.new(); stdin->fs.fd = 0; + stdin->fs.importdesc(); stdin->flags = File.F_GLOBAL; stdin->mode = File.M_LINE; stdin->autosetduplex(); stdout.new(); stdout->fs.fd = 1; + stdout->fs.importdesc(); stdout->flags = File.F_GLOBAL; stdout->mode = File.M_LINE; stdout->autosetduplex(); stderr.new(); stderr->fs.fd = 2; + stderr->fs.importdesc(); stderr->flags = File.F_GLOBAL; stderr->mode = File.M_LINE; stderr->autosetduplex(); diff --git a/classes/sys/fd.d b/classes/sys/fd.d index 62bfc88..d258fad 100644 --- a/classes/sys/fd.d +++ b/classes/sys/fd.d @@ -34,6 +34,7 @@ # write - write the full request or until an error. # write1- write at least 1 byte plus whatever else it can. # writen- non-blocking write can return on EINTR or EAGAIN. +# importdesc - adjust imported descriptor for rune operation # public class Fs { public int fd = -1; # field must match libruntime @@ -77,6 +78,7 @@ public class Fs { public internal method int setappend(bool enable); public internal method int setnonblock(bool enable); public internal method int setdirect(bool enable); + public internal method void importdesc(); public internal method void iowait_rd(); public internal method void iowait_wr(); diff --git a/classes/sys/stream.d b/classes/sys/stream.d index 0f5bffe..41c31c1 100644 --- a/classes/sys/stream.d +++ b/classes/sys/stream.d @@ -31,6 +31,18 @@ public class Stream { # public internal method int shutdown(int how); } +public +Stream * +Stream.fdopen(int fd, const char *mode) +{ + Stream *s; + + s.new(); + s->file.fdreopen(fd, mode); + + return s; +} + #%doc # # This class supports half-duplex and full-duplex streaming pairs. You can @@ -42,8 +54,8 @@ public class Stream { # Stream descriptors, each descriptor can be both read and written. # public class StreamPair { - public Stream srd; - public Stream swr; + public Stream *srd; + public Stream *swr; } public method @@ -55,8 +67,8 @@ StreamPair.pipehdx() $ = Fs.pipe(srd, swr); if ($ >= 0) { - this.srd.file.fdreopen(srd.fd, "r"); - this.swr.file.fdreopen(swr.fd, "w"); + this.srd = Stream.fdopen(srd.fd, "r"); + this.swr = Stream.fdopen(swr.fd, "w"); } } @@ -76,9 +88,9 @@ StreamPair.socketpair(int d, int type, int proto) $ = Fs.socketpair(d, type | SOCK_CLOEXEC, proto, srd, swr); if ($ >= 0) { - this.srd.file.fdreopen(srd.fd, "r+"); - this.srd.file.setbidir(TRUE); - this.swr.file.fdreopen(swr.fd, "r+"); - this.swr.file.setbidir(TRUE); + this.srd = Stream.fdopen(srd.fd, "r+"); + this.srd->file.setbidir(TRUE); + this.swr = Stream.fdopen(swr.fd, "r+"); + this.swr->file.setbidir(TRUE); } } diff --git a/libgen/syscalls.c b/libgen/syscalls.c index c4040a1..5dc7953 100644 --- a/libgen/syscalls.c +++ b/libgen/syscalls.c @@ -223,6 +223,7 @@ GENINTERNALCALL(setcloexec) GENINTERNALCALL(setappend) GENINTERNALCALL(setnonblock) GENINTERNALCALL(setdirect) +GENINTERNALCALL(importdesc) GENINTERNALCALL(pipe) GENINTERNALCALL(socketpair) @@ -426,6 +427,11 @@ static Syscall SyscallAry[] = { .sc_GenFunc = gen_setdirect, .sc_Id = NULL }, + { .sc_Name = "importdesc", + .sc_RunFunc = (sys_run_t)RuneSysCall_importdesc, + .sc_GenFunc = gen_importdesc, + .sc_Id = NULL + }, { .sc_Name = "pipe", .sc_RunFunc = (sys_run_t)RuneSysCall_pipe, .sc_GenFunc = gen_pipe, diff --git a/libruntime/event.c b/libruntime/event.c index 5a8bc56..82a5c5d 100644 --- a/libruntime/event.c +++ b/libruntime/event.c @@ -5,8 +5,6 @@ #include "defs.h" #include -static int MaxFd = 0; - /* * NOTE: We want to be able to accomodate potentially several million * rune threads, so be careful not to undersize the hash tables. @@ -115,20 +113,23 @@ threadPollEvent(int block) switch(kev->filter) { case EVFILT_READ: - if ((int)kev->ident >= MaxFd) + if ((uint32_t)kev->ident >= (uint32_t)rgd->MaxFd) continue; - if ((list = rgd->TaskReadAry[kev->ident]) != NULL) + list = &rgd->TaskReadAry[kev->ident]; + if (RUNE_FIRST(list)) threadWakeupList(list, &rgd->KQueueMtx, 0); break; case EVFILT_WRITE: - if ((int)kev->ident >= MaxFd) + if ((uint32_t)kev->ident >= (uint32_t)rgd->MaxFd) continue; - if ((list = rgd->TaskWriteAry[kev->ident]) != NULL) + list = &rgd->TaskWriteAry[kev->ident]; + if (RUNE_FIRST(list)) threadWakeupList(list, &rgd->KQueueMtx, 0); break; case EVFILT_PROC: - list = rgd->TaskProc; - threadWakeupList(list, &rgd->KQueueMtx, 0); + list = &rgd->TaskProc; + if (RUNE_FIRST(list)) + threadWakeupList(list, &rgd->KQueueMtx, 0); break; } } @@ -169,7 +170,7 @@ int threadWaitEvent(intptr_t ident, int how) { rgd_t *rgd = getrgd(); - tdlist_t **plist; + tdlist_t *list; int filter = -1; int r; u_int fflags = 0; @@ -179,44 +180,43 @@ threadWaitEvent(intptr_t ident, int how) threadMutexLock(&rgd->KQueueMtx); dassert(how == THWAIT_PROC || ident >= 0); - if ((how == THWAIT_READ || how == THWAIT_WRITE) && ident >= MaxFd) { + if ((how == THWAIT_READ || how == THWAIT_WRITE) && + ident >= rgd->MaxFd) { int nmax = ident + 1; if (ident > RUNE_FD_MAX) dpanic("File descriptor %zd out of bounds", ident); - if (nmax < MaxFd * 2) - nmax = MaxFd * 2; + if (nmax < rgd->MaxFd * 2) + nmax = rgd->MaxFd * 2; + if (nmax > RUNE_FD_MAX) + nmax = RUNE_FD_MAX; rgd->TaskReadAry = zrealloc(rgd->TaskReadAry, - MaxFd * sizeof(tdlist_t *), - nmax * sizeof(tdlist_t *)); + rgd->MaxFd * sizeof(tdlist_t), + nmax * sizeof(tdlist_t)); rgd->TaskWriteAry = zrealloc(rgd->TaskWriteAry, - MaxFd * sizeof(tdlist_t *), - nmax * sizeof(tdlist_t *)); - MaxFd = nmax; + rgd->MaxFd * sizeof(tdlist_t), + nmax * sizeof(tdlist_t)); + rgd->MaxFd = nmax; } switch(how) { case THWAIT_READ: filter = EVFILT_READ; - plist = &rgd->TaskReadAry[ident]; + list = &rgd->TaskReadAry[ident]; break; case THWAIT_WRITE: filter = EVFILT_WRITE; - plist = &rgd->TaskWriteAry[ident]; + list = &rgd->TaskWriteAry[ident]; break; case THWAIT_PROC: filter = EVFILT_PROC; - plist = &rgd->TaskProc; + list = &rgd->TaskProc; fflags = NOTE_EXIT; break; default: threadMutexUnlock(&rgd->KQueueMtx); return -1; } - if (*plist == NULL) { - *plist = zalloc(sizeof(tdlist_t)); - RUNE_INIT(*plist); - } /* * XXX will adding an event wakeup a blocked kevent() call in another @@ -230,7 +230,7 @@ threadWaitEvent(intptr_t ident, int how) pthread_kill(rgd->BlockingTid, SIGUSR2); } #endif - threadStop(*plist, THF_EVENTBLOCKED, &rgd->KQueueMtx); + threadStop(list, THF_EVENTBLOCKED, &rgd->KQueueMtx); /* rgd invalid */ return r; diff --git a/libruntime/sys_fd.c b/libruntime/sys_fd.c index 669c0f6..bb06781 100644 --- a/libruntime/sys_fd.c +++ b/libruntime/sys_fd.c @@ -102,6 +102,29 @@ struct unlink_args { PointerStor target; }; +/* + * It is preferable to specify non-blocking operation on a call-by-call basis, + * otherwise we have to set non-blocking mode on the descriptor which can + * cause problems with shared file pointers (such as for ttys). + */ +#ifdef _HAVE_EXTPREAD + +#define nbread(fd, buf, bytes, offset) \ + extpread(fd, buf, bytes, O_FNONBLOCKING, offset) +#define nbwrite(fd, buf, bytes, offset) \ + extpwrite(fd, buf, bytes, O_FNONBLOCKING, offset) +#define nbiosetup(fd) + +#else + +#define nbread(fd, buf, bytes, offset) \ + pread(fd, buf, bytes, offset) +#define nbwrite(fd, buf, bytes, offset) \ + pwrite(fd, buf, bytes, offset) +#define nbiosetup(fd) fcntl(fd, F_SETFL, O_NONBLOCK); + +#endif + /* * Normal read */ @@ -120,13 +143,11 @@ RuneSysCall_read(struct read_args *args, runesize_t *rval) r = 0; while (r < args->bytes) { if (args->offset < 0) { - n = extpread(fdp->fd, args->buf.s_Addr, - args->bytes - r, - O_FNONBLOCKING, args->offset); + n = nbread(fdp->fd, args->buf.s_Addr, + args->bytes - r, args->offset); } else { - n = extpread(fdp->fd, args->buf.s_Addr, - args->bytes - r, - O_FNONBLOCKING, args->offset + r); + n = nbread(fdp->fd, args->buf.s_Addr, + args->bytes - r, args->offset + r); } if (n < 0) { if (errno == EINTR) @@ -166,8 +187,7 @@ RuneSysCall_read1(struct read_args *args, runesize_t *rval) BOUNDSCHECK(&args->buf, args->bytes); fdp = (void *)args->lvs.s_Addr; again: - *rval = extpread(fdp->fd, args->buf.s_Addr, args->bytes, - O_FNONBLOCKING, args->offset); + *rval = nbread(fdp->fd, args->buf.s_Addr, args->bytes, args->offset); if (*rval < 0) { if (errno == EINTR) @@ -196,8 +216,7 @@ RuneSysCall_readn(struct read_args *args, runesize_t *rval) BOUNDSCHECK(&args->buf, args->bytes); fdp = (void *)args->lvs.s_Addr; - *rval = extpread(fdp->fd, args->buf.s_Addr, args->bytes, - O_FNONBLOCKING, args->offset); + *rval = nbread(fdp->fd, args->buf.s_Addr, args->bytes, args->offset); if (*rval < 0) { fdp->error = errno; @@ -221,11 +240,11 @@ RuneSysCall_write(struct write_args *args, runesize_t *rval) r = 0; while (r < args->bytes) { if (args->offset < 0) { - n = extpwrite(fdp->fd, ptr, args->bytes - r, - O_FNONBLOCKING, args->offset); + n = nbwrite(fdp->fd, ptr, args->bytes - r, + args->offset); } else { - n = extpwrite(fdp->fd, ptr, args->bytes - r, - O_FNONBLOCKING, args->offset + r); + n = nbwrite(fdp->fd, ptr, args->bytes - r, + args->offset + r); } if (n < 0) { if (errno == EINTR) @@ -264,8 +283,7 @@ RuneSysCall_write1(struct write_args *args, runesize_t *rval) fdp = (void *)args->lvs.s_Addr; again: - *rval = extpwrite(fdp->fd, args->buf.s_Addr, args->bytes, - O_FNONBLOCKING, args->offset); + *rval = nbwrite(fdp->fd, args->buf.s_Addr, args->bytes, args->offset); if (*rval < 0) { if (errno == EINTR) @@ -291,8 +309,7 @@ RuneSysCall_writen(struct write_args *args, runesize_t *rval) BOUNDSCHECK(&args->buf, args->bytes); fdp = (void *)args->lvs.s_Addr; - *rval = extpwrite(fdp->fd, args->buf.s_Addr, args->bytes, - O_FNONBLOCKING, args->offset); + *rval = nbwrite(fdp->fd, args->buf.s_Addr, args->bytes, args->offset); if (*rval < 0) { fdp->error = errno; @@ -315,6 +332,7 @@ RuneSysCall_open(struct open_args *args, int *rval) fdp->error = errno; *rval = -1; } else { + nbiosetup(fdp->fd); fdp->error = 0; *rval = 0; } @@ -462,6 +480,15 @@ RuneSysCall_setdirect(struct fsimple_args *args, int *rval) } } +void +RuneSysCall_importdesc(struct fsimple_args *args, int *rval) +{ + FDStor *fdp __unused; + + fdp = (void *)args->lvs.s_Addr; + nbiosetup(fdp->fd); +} + void RuneSysCall_iowait_rd(struct iowait_args *args, void *rval) { @@ -504,6 +531,8 @@ RuneSysCall_pipe(struct pipe_args *args, int *rval) fdr->error = 0; fdw->fd = fds[1]; fdw->error = 0; + nbiosetup(fdr->fd); + nbiosetup(fdw->fd); *rval = 0; } } @@ -527,6 +556,8 @@ RuneSysCall_socketpair(struct socketpair_args *args, int *rval) fdr->error = 0; fdw->fd = fds[1]; fdw->error = 0; + nbiosetup(fdr->fd); + nbiosetup(fdw->fd); *rval = 0; } } diff --git a/libruntime/sys_thread.c b/libruntime/sys_thread.c index e2ebd09..ceedd92 100644 --- a/libruntime/sys_thread.c +++ b/libruntime/sys_thread.c @@ -36,6 +36,7 @@ RuneSysCall_waitThreads(void *args, void *rval) { fflush(stdout); threadWaitTerminate(); + /* prior rgd invalid */ } void diff --git a/libruntime/syscalls.h b/libruntime/syscalls.h index a58db77..7fb0f8c 100644 --- a/libruntime/syscalls.h +++ b/libruntime/syscalls.h @@ -70,6 +70,7 @@ void RuneSysCall_setcloexec(struct fsimple_args *args, int *rval); void RuneSysCall_setappend(struct fsimple_args *args, int *rval); void RuneSysCall_setnonblock(struct fsimple_args *args, int *rval); void RuneSysCall_setdirect(struct fsimple_args *args, int *rval); +void RuneSysCall_importdesc(struct fsimple_args *args, int *rval); void RuneSysCall_pipe(struct pipe_args *args, int *rval); void RuneSysCall_socketpair(struct socketpair_args *args, int *rval); diff --git a/libruntime/thread.c b/libruntime/thread.c index d0bf782..79b64ee 100644 --- a/libruntime/thread.c +++ b/libruntime/thread.c @@ -128,33 +128,8 @@ threadEnvironmentStart(int smp) td->td_Flags |= THF_RUNNING; atomic_add_rune(&NumThreads, 1); rgd->CurThread = td; - -#if 0 - if (IsSMP) { - pthread_join(MainPTD, NULL); - dassert(rgd->CurThread == NULL); - } else { - td = RUNE_FIRST(&rgd->RunQueue); - dassert(td); - RUNE_REMOVE(&rgd->RunQueue, td, td_Node); - rgd->CurThread = td; - RunThreads = 1; - if (sigsetjmp(MainEnv, 0) == 0) { - threadMutexLock(&rgd->SchedMtx); - siglongjmp(rgd->CurThread->td_Env, 1); - /* not reached */ - } - /* reload invalid rgd after setjmp */ - rgd = getrgd(); - dassert(rgd->CurThread == rgd->IdleThread); - rgd->CurThread = NULL; - } -#endif - - /* - * Returns to main environment after the last thread - * has stopped. - */ + RunThreads = 1; + /* becomes the first thread */ } static @@ -495,8 +470,9 @@ threadWaitTerminate(void) threadMutexLock(&PThreadMtx); atomic_add_rune(&NumThreads, -1); if (NumThreads) { - fprintf(stderr, "Waiting for %zd threads to end\n", NumThreads); threadStop(&LastQueue, 0, &PThreadMtx); + /* prior rgd invalid */ + fflush(stdout); } else { threadMutexUnlock(&PThreadMtx); } @@ -531,6 +507,7 @@ threadStop(tdlist_t *list, int flags, pthread_mutex_t *mtx) dassert(td != rgd->IdleThread); dassert(td->td_Flags & THF_RUNNING); RUNE_INSERT_TAIL(list, td, td_Node); + atomic_add_rune(&RunThreads, -1); threadMutexUnlock(mtx); /* @@ -587,7 +564,6 @@ threadStop(tdlist_t *list, int flags, pthread_mutex_t *mtx) * Switch away */ threadMutexLock(&rgd->SchedMtx); - atomic_add_rune(&RunThreads, -1); rgd->CurThread = NULL; while ((tdnext = RUNE_FIRST(&rgd->RunQueue)) == NULL) { if (NumThreads) { @@ -595,7 +571,6 @@ threadStop(tdlist_t *list, int flags, pthread_mutex_t *mtx) goto skip; } if (RUNE_FIRST(&LastQueue)) { - printf("LASTQUEUE\n"); /* XXX */ threadMutexUnlock(&rgd->SchedMtx); @@ -723,10 +698,10 @@ threadStart(runethr_t *td, pthread_mutex_t *lmtx) * td can be ripped out from under us, but we can safely * test the pointer value. */ - if (IsSMP) { + if (IsSMP && tgd != getrgd()) { threadMutexUnlock(lmtx); - if (RUNE_FIRST(&tgd->RunQueue) != td) - threadPushRunnable(tgd); + /*if (RUNE_FIRST(&tgd->RunQueue) != td)*/ + threadPushRunnable(tgd); threadMutexUnlock(&tgd->SchedMtx); threadMutexLock(lmtx); } else { @@ -837,8 +812,8 @@ threadSwitch(void) if ((td->td_Flags & THF_IDLE) == 0) { RUNE_INSERT_TAIL(&rgd->RunQueue, td, td_Node); if (IsSMP) { - if (RUNE_FIRST(&rgd->RunQueue) != td) - threadPushRunnable(rgd); + /*if (RUNE_FIRST(&rgd->RunQueue) != td)*/ + threadPushRunnable(rgd); } } @@ -927,8 +902,6 @@ threadPushRunnable(rgd_t *rgd) threadMutexUnlock(&rgd->SchedMtx); threadMutexLock(&PThreadMtx); for (scan = RUNE_FIRST(&GDList); scan; scan = RUNE_NEXT(scan, Node)) { - if (scan == rgd) - continue; if (scan->BlockingTid) { threadMutexUnlock(&PThreadMtx); /* XXX */ threadMutexLock(&scan->KQueueMtx); @@ -975,27 +948,29 @@ threadIdleFunc(void *data) threadMutexUnlock(&PThreadMtx); } - if (NumThreads == 0 || RUNE_FIRST(&rgd->WakeupQueue)) { + if (RUNE_FIRST(&rgd->RunQueue) || + RUNE_FIRST(&rgd->WakeupQueue)) { /* - * If no threads remain switch away for LastQueue - * processing. Also switch away immediately if the - * WakeupQueue is not empty. + * Runnable threads are available. */ threadWakeupList(&rgd->WakeupQueue, NULL, 0); } else if (IsSMP && threadPullRunnable(rgd)) { - /* switch to runnable thread pulled from another gd */ + /* + * Runnable threads are available after pulling + * one from another rgd. + */ } else if (IsSMP) { /* - * BLOCK THE PTHREAD HERE + * SMP mode (multiple rgds). * - * We call threadPollEvent() to block the current - * pthread when no rune threads are runnable. + * Call threadPollEvent() in a blockable fashion. */ threadPollEvent(1); } else { /* - * In single-pthread mode we are the only idle thread - * so it is up to us to handle blocking polls. + * UP mode (single rgd, interpreter or debugging) + * + * Call threadPollEvent() in a blockable fashion. */ threadPollEvent(1); } diff --git a/libruntime/thread.h b/libruntime/thread.h index fdd0f34..f3f9a2f 100644 --- a/libruntime/thread.h +++ b/libruntime/thread.h @@ -96,6 +96,8 @@ struct RuneGlobalData { tdlist_t WakeupQueue; int EVCount; int PullIterator; + int MaxFd; + int Unused01; runesize_t FreeCount; sigjmp_buf RetEnv; pthread_mutex_t SchedMtx; @@ -106,9 +108,9 @@ struct RuneGlobalData { */ int KQueue; struct timeval SaveTv; - tdlist_t **TaskReadAry; - tdlist_t **TaskWriteAry; - tdlist_t *TaskProc; + tdlist_t *TaskReadAry; + tdlist_t *TaskWriteAry; + tdlist_t TaskProc; timerlist_t TimerList; pthread_mutex_t KQueueMtx; pthread_t BlockingTid; diff --git a/tests/pipe1.d b/tests/pipe1.d index 9baaf04..a32f191 100755 --- a/tests/pipe1.d +++ b/tests/pipe1.d @@ -18,41 +18,46 @@ main(int ac, char **av) strm.pipehdx(); strm.reader(); strm.writer(); + stdio.stderr->format("waiting for threads to exit\n"); Thread.waitThreads(); + stdio.stderr->format("threads have exited\n"); } thread method void MyStuff.reader() { - stdio.File @fp = &this.srd.file; + Stream @srd = this.srd; char *buf; size_t len; result; stdio.stderr->format("reader begin\n"); - while ((buf = fp->fgetbuf(&len)) != NULL) { + return; + while ((buf = srd->file.fgetbuf(&len)) != NULL) { + stdio.stderr->format("reader data\n"); stdio.stdout->fwrite(buf, len); stdio.stdout->fflush(); } - fp->fclose(); - stdio.stderr->format("reader finished error=%d\n", fp->fs.error); + srd->file.fclose(); + stdio.stderr->format("reader finished error=%d\n", srd->file.fs.error); } thread method void MyStuff.writer() { - stdio.File @fp = &this.swr.file; + Stream @swr = this.swr; char *buf; size_t len; result; stdio.stderr->format("writer begin (line buffered)\n"); while ((buf = stdio.stdin->fgetbuf(&len)) != NULL) { - fp->fwrite(buf, len); - fp->fflush(); + stdio.stderr->format("writer data\n"); + swr->file.fwrite(buf, len); + swr->file.fflush(); } stdio.stderr->format("writer finished\n"); - fp->fclose(); + swr->file.fclose(); } diff --git a/tests/threadov.d b/tests/threadov.d index 40ccc80..da03a10 100755 --- a/tests/threadov.d +++ b/tests/threadov.d @@ -59,6 +59,7 @@ void test3(int i) { int l = i; + int j; result; @@ -67,10 +68,13 @@ test3(int i) stdout->format("thread %d\n", l); for (;;) { - Thread.mssleep(100); - if (l == 0) + Thread.mssleep(1); + if (l % 10000 == 0) { stdout->format("loop %d\n", l); - else + Thread.mssleep(1000); + continue; + } + for (j = 0; j < 5000; ++j) ; } } diff --git a/tests/ttest.c b/tests/ttest.c index b26237f..ab5c42a 100644 --- a/tests/ttest.c +++ b/tests/ttest.c @@ -18,6 +18,7 @@ main(int ac, char **av) threadEnvironmentStart(0); envstart(NULL); threadWaitTerminate(); + /* prior rgd invalid */ puts("main done"); return(0); } -- 2.41.0