Hi folks,

Due to my new found belief that all of the flaws in Wine
are timing problems, I have found what appears to be a
gaping hole in Wine's timing behavior.

Specifically, it appears as though any style of
Waitxxx is supposed to yield the processor.

This seems a bit difficult for me to believe,
even though we had earlier discovered that Wine was never
yielding on a Sleep(0).

I've attached the test program I used to discover
this.  If you run
  sleep2 -1 0w
the program will start 2 threads, one which spin loops
(-1), and one which does a wait on an object for 0
seconds.  You'll see in the results that the Wait(...,0)
thread gets far fewer loops, and thus must be yielding.

Further, changing that to a 0ws (or even 5ws), which
means we're waiting on a signalled object, doesn't
change the behavior.

This leads me to suggest the attached patch.

I would appreciate review from other folks, because this
seems like a drastic change to me, and it strikes
me as equally likely that I've done something stupid.

Cheers,

Jeremy
#include <STDIO.H>
#include <stdlib.h>
#include <windows.h>

typedef struct {
    char *arg;
    HANDLE event;
    HANDLE handle;
    DWORD stopat;
    int wait;
    int signaled;
    int priority;
    int sleeptime;
} threadinfo;

int quit = 0;
HANDLE releaseall;
DWORD WINAPI mythread(LPVOID myparm)
{
    threadinfo *info = (threadinfo *) myparm;
    DWORD last, now;
    DWORD delta, max_delta = 0, total_delta = 0;
    long count = 0;
    long delta_count = 0;
    long expected_loops = -1;
    int rc;

    if (info->sleeptime > 0)
        expected_loops = 5000 / info->sleeptime;

    WaitForSingleObject(releaseall, INFINITE);
    while (! quit)
    {
        last = GetTickCount();
        if (last > info->stopat)
            break;

        count++;
        if (info->sleeptime >= 0)
        {
            if (info->wait)
            {
                rc = WaitForSingleObject(info->event, info->sleeptime);
                if (info->signaled && rc != WAIT_OBJECT_0)
                    printf("whoops!\n");
            }
            else
                Sleep(info->sleeptime);

            now = GetTickCount();
            if (now - last != (DWORD) info->sleeptime)
            {
                delta_count++;
                delta = now - last;
                total_delta += delta;
                if (delta > max_delta)
                    max_delta = delta;
            }
        }
    }

    printf("%s: [%ld loops|%ld expected|%lu total_delta|%lu max delta|%ld delta_count|%ld avg delta]\n",
        info->arg, count, expected_loops, total_delta, max_delta, delta_count, delta_count == 0 ? 0 : total_delta / delta_count);

    return(0);
}


int main(int argc, char *argv[])
{
    int i;
    char *p;
    DWORD tid;
    threadinfo threads[20];
    DWORD dieat;

    releaseall = CreateEvent(NULL, TRUE, FALSE, NULL);

    dieat = GetTickCount() + 5000;

    memset(threads, '\0', sizeof(threads));
    for (i = 1; i < argc && i < sizeof(threads) / sizeof(threads[0]); i++)
    {
        threads[i].sleeptime = atoi(argv[i]);
        if (strchr(argv[i], 's'))
            threads[i].signaled++;
        threads[i].handle = CreateThread(NULL, 0, &mythread, (void *)&threads[i], 0, &tid);
        threads[i].event = CreateEvent(NULL, threads[i].signaled ? TRUE : FALSE, 
                                threads[i].signaled ? TRUE : FALSE, NULL);
        threads[i].stopat = dieat;
        threads[i].arg = argv[i];
        if (strchr(argv[i], 'w'))
            threads[i].wait++;
        p = strchr(argv[i], ':');
        if (p)
        {
            threads[i].priority = atoi(p + 1);
            SetThreadPriority(threads[i].handle, threads[i].priority);
        }

        p = strchr(argv[i], 'e');
        if (p)
        {
            timeSetEvent(threads[i].sleeptime, 1, threads[i].event, 0, TIME_PERIODIC |
                    TIME_CALLBACK_EVENT_SET);
        }

    }

    printf("Starting threads, waiting for 5 s\n");
    fflush(stdout);
    SetEvent(releaseall);

    Sleep(5000);

    quit = 1;
    Sleep(1000);
}
Index: dlls/ntdll/sync.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/sync.c,v
retrieving revision 1.37
diff -u -r1.37 sync.c
--- dlls/ntdll/sync.c	11 Oct 2004 20:11:01 -0000	1.37
+++ dlls/ntdll/sync.c	1 Nov 2004 20:59:47 -0000
@@ -586,6 +586,11 @@
         call_apcs( (flags & SELECT_ALERTABLE) != 0 );
         if (flags & SELECT_ALERTABLE) break;
     }
+
+    /* A test on Windows 2000 shows that Windows always yields at the
+       end of a wait, regardless of the disposition of the wait itself */
+    NtYieldExecution();
+
     return ret;
 }
 

Reply via email to