Implement SetThreadPriority using sched_setscheduler()

Mike Hearn mike at plan99.net
Thu Mar 30 17:05:51 CST 2006


This patch gives me rock solid audio in Imperium Galactica 2. No more 
white noise, clicking or destabilising half way through.

It exposes a race that I'll send a fix for later, for now I am hacking 
around it with a sleep(1) at the end of RtlUserCreateThread. The race is 
that you can do this:

    hthread = CreateThread(...);
    SetThreadPriority(hthread ...);

and the set_thread_info server call will occur before the thread has 
fully initialized, so, we don't know the UNIX tid and cannot set the 
priority. My current idea is to fix the race by forcing 
RtlUserCreateThread to wait for the thread to sync with the server using 
an event.

This patch only implements support for TIME_CRITICAL threads. Mapping 
priorities at a finer level of granularity is still on the todo list.

Final thing - there has been on/off discussion about the security 
implications of this in the past. But, this code needs to be implemented 
anyway, regardless of outcome, and on SuSE at least I think AppArmor can 
be used to allow for thread prio raising without full root access. So 
IMHO the permissions debate layers on top of this. Until an acceptable 
solution is found some users may wish to just run Wine as root and play 
their games, which they know are trustable.

thanks -mike



-------------- next part --------------
diff --git a/server/thread.c b/server/thread.c
index 1c00ce7..eeb9929 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -32,6 +32,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <time.h>
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
 #ifdef HAVE_POLL_H
 #include <poll.h>
 #endif
@@ -319,7 +322,36 @@ static void set_thread_info( struct thre
                              const struct set_thread_info_request *req )
 {
     if (req->mask & SET_THREAD_INFO_PRIORITY)
+    {
+#ifdef HAVE_SCHED_H
+        if ((req->priority == THREAD_PRIORITY_TIME_CRITICAL) && (thread->unix_tid != -1))
+        {
+            struct sched_param param;
+            int result;
+
+            param.sched_priority = 1;
+            result = sched_setscheduler(thread->unix_tid, SCHED_FIFO, &param);
+
+            if ((result < 0) && (result != -EPERM))
+            {
+                perror("wineserver: sched_setscheduler");
+            }
+            else if (result == -EPERM)
+            {
+                static int need_warning = 1;
+                
+                if (need_warning)
+                {
+                    fprintf(stderr, "\nwineserver: Failed to promote the priority of a time critical thread.\n");
+                    fprintf(stderr, "Audio may destabilise. To fix this re-run the application as root.\n\n");
+                    need_warning = 0;
+                }
+            }
+        }
+#endif
         thread->priority = req->priority;
+    }
+
     if (req->mask & SET_THREAD_INFO_AFFINITY)
     {
         if (req->affinity != 1) set_error( STATUS_INVALID_PARAMETER );


More information about the wine-patches mailing list