1

I am trying to use Pthreads to set priorities and scheduling policies.

I modified a simple socket (downloadable online), with a server and several clients. These clients are simply sending a string to the server.

The server is creating a new thread for each client connecting and is setting a higher priority to every new connection, which means the last client connected is the one with the highest priority. The result I am expecting is that the thread with highest priority is the one printing out until that client is connected, while the others should wait.

However, this is not the result I am getting. The threads are all executed anyway, even the ones with lower priorities. I even tried several configurations, for instance using pthread_join, but in this case the scheduler will wait until that thread has finished its execution, which is wrong if another thread with higher priority is called (in my example another client connecting), since it should release the CPU to this newly created thread.

Here is the code:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>

#define PORTNUMBER 8888

int prioInit = 30; //global variable to initialize priority

struct serverParm 
{
       int connectionDesc;
};

//function used to increase priority to new threads
void addPrio(int *prio)
{
  *prio = *prio + 10;
}

void *serverThread(void *parmPtr) 
{
  #define PARMPTR ((struct serverParm *) parmPtr)
  int recievedMsgLen;
  char messageBuf[1025];

  struct sched_param Priority_Param; //struct to set priority
  int policy=SCHED_FIFO; //kind of policy desired, either SCHED_FIFO or SCHED_RR, otherwise Linux uses SCHED_OTHER

  // Server thread code to deal with message processing
  printf("DEBUG: connection made, connectionDesc=%d\n",
        PARMPTR->connectionDesc);

  pthread_t self_id= pthread_self(); // I ask for the tid..
  Priority_Param.sched_priority = prioInit; //.. set the priority (the higher the sooner it is executed, min at 1, max at 99)..
  addPrio(&prioInit); //..increase the base priority..
  if(pthread_setschedparam(self_id, policy, &Priority_Param) != 0) //.. and set the scheduling options.
  {
    printf("Error Set Sched\n");
    perror("");
    exit(1);
  }

  if (PARMPTR->connectionDesc < 0) 
  {
    printf("Accept failed\n");
    exit(1);   
  }

  // Receive messages from sender
  while ((recievedMsgLen=
        read(PARMPTR->connectionDesc,messageBuf,sizeof(messageBuf)-1)) > 0) 
  {
    recievedMsgLen[messageBuf] = '\0';
    printf("Message: %s\n",messageBuf);
  }
  close(PARMPTR->connectionDesc);  // Avoid descriptor leaks 
  free(PARMPTR);                   // And memory leaks                        
}

int main () 
{
  int listenDesc;
  struct sockaddr_in myAddr;
  struct serverParm *parmPtr;
  int connectionDesc;
  pthread_t threadID;

  // Create socket from which to read 
  if ((listenDesc = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
  {
    perror("open error on socket");
    exit(1);
  }

  // Create "name" of socket
  myAddr.sin_family = AF_INET;
  myAddr.sin_addr.s_addr = INADDR_ANY;
  myAddr.sin_port = htons(PORTNUMBER);

  if (bind(listenDesc, (struct sockaddr *) &myAddr, sizeof(myAddr)) < 0) 
  {
    perror("bind error");
    exit(1);
  }

  // Start accepting connections
  listen(listenDesc,5);

  while (1)
  {
    // Wait for a client connection
    connectionDesc = accept(listenDesc, NULL, NULL);

    // Create a thread to actually handle this client
    parmPtr = (struct serverParm *)malloc(sizeof(struct serverParm));
    parmPtr->connectionDesc = connectionDesc;
    if (pthread_create(&threadID, NULL, serverThread, (void *)parmPtr) 
          != 0) 
    {
      perror("Thread create error");
      close(connectionDesc);
      close(listenDesc);
      exit(1);
    }

    printf("Parent ready for another connection\n");
  }

}

I am compiling using -pthread option and running as root, so that the policy and priority is set and changed. I am for sure missing something here, but I am also wondering if it is possible to really use and change the scheduling options, in order to have a behavior similar to the real-time one.

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
Stef
  • 11
  • 1
  • 1
  • 3
  • Of course the other lower-priority threads get some time, doing just about any kind of I/O (like reading from a socket) will preempt the thread until the I/O is done, allowing the lower priority threads to get a *little* done. – Some programmer dude Sep 29 '15 at 08:17
  • I agree, but I tried even without I/O, for instance creating two threads with different priorities which are strictly increasing a counter; the result is still not the expected one. The thread which is created before is executed till the end, while the others, even if with higher priority, waits, which is not correct. Thats is why I am wondering where I am wrong, or perhaps it means we are not able to completely control the scheduler, it will anyway execute what the CPU "thinks" is best for the system, besides the priority set. – Stef Sep 29 '15 at 08:24
  • Oh by the way, [in C you should not cast the result of `malloc`](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – Some programmer dude Sep 29 '15 at 08:29
  • Your threads don't really do anything - they block waiting for input, dump what they read out, then immediately block waiting for more input. – Andrew Henle Sep 29 '15 at 10:19
  • Yes I know and that's the point, it is just a test, in order to see if I can handle interrupts, coming from different threads, with pthread. The point is that I don't know how else I can test pthread and see if the scheduler is actually doing what I am setting as policy and priority, that is why this code is not really doing much else than that. Do you have a way to test pthread in multithreading? For instance to see if I am actually acting as a real-time machine? I tried to look over the net before asking but didn't find much unfortunately. – Stef Sep 29 '15 at 10:31
  • @Stef You need to understand that blocking `read` syscall blocks a thread regardless of its priority when there is no data to read in the kernel socket buffer. – Maxim Egorushkin Sep 29 '15 at 10:56
  • @MaximEgorushkin, thanks, you're right, I agree :) Anyway this is just one of several tests that I have done, for instance I even created one simple program with two threads (besides main ofc) with different priorities (changing also the policy obviously), which were just using a printf in a for loop. Still the result is the same as here, regardless from the policy or priority. What I am trying to ask is: is there a way to have real-time behavior on a non real-time machine? It's hard to understand with pthread, also cause I haven't found any real example where I can see the evidence. – Stef Sep 29 '15 at 11:49
  • What is your goal, what do you optimize for? – Maxim Egorushkin Sep 29 '15 at 12:09
  • My goal is to see if it is possible to have a behavior similar to real-time in a standard Linux machine (non real-time) or if it is necessary to apply a real-time patch. In order to see that I created some simple examples like the one posted here, in order to have a better view and understanding of pthread. If I understand that this is possible then I would apply this to a project working with I/O interrupts; that would be something to optimize and work for, but just after I realized that my goal is possible. Like I said, I apologize if I am missing something and thanks for your help. – Stef Sep 29 '15 at 12:28
  • Your use of printf in the server thread loop will serialise the threads. Printf uses stdout, and the implementation of stdout these days is rather complicated. It involves a pipe, and a buffer. Two things trying to write to stdout at the same time get run sequentially, not consecutively. – bazza Sep 30 '15 at 05:03
  • @bazza I know, that is why I wrote here, also to get a better example than mine. :) – Stef Sep 30 '15 at 06:58

2 Answers2

1

Scheduler policy and priority is used to determine which thread will run when a choice must be made between two or more runnable threads.

If your high priority thread blocks in read(), then it is not eligible to run, and a lower priority thread that is runnable will get a chance to run instead. What the priority means is that even all of the available CPU cores are running lower priority threads at the moment when data arrives from the network for the higher priority thread, a lower priority thread will be immediately pre-empted so that the higher priority thread can run.

If you want to see the effect of priority, have a lower-priority thread do lots of work so that it is almost always using the CPU: your higher-priority thread should still respond immediately to network input.

(But if you do this, either use a non-realtime priority level for the lower-priority threads, or make sure you have a shell open that is running at an elevated realtime priority, so that the lower-priority thread doesn't block your shell from running when you need it).

Side note: your addPrio() function is racy - it should lock a mutex around the changes to *prio.

caf
  • 233,326
  • 40
  • 323
  • 462
  • thanks for your reply, I actually tried a thread using and increasing just a counter, with no I/O and in that case; even if I stop the counter at a really high value, and regardless the priority and policy, I still get all the job done in the counter thread and just afterwards my rt threads are created and executed. That is why I asked this, cause I can't be sure if pthread_setschedparam is actually effective and if there is a way to test it which is certain, so that I can answer my question :) – Stef Sep 30 '15 at 06:37
  • @Stef: If you have a different test program, you should post a new question about that one. – caf Sep 30 '15 at 06:46
  • that would be useless I think, since the problem would still be the same as here. And I'm here also to have suggestions about another possible test program, if anyone have tried or experienced this – Stef Oct 02 '15 at 06:53
  • Stef: No, the reason *this* program schedules the low priority threads is that the high priority threads spend a lot of time sleeping, as outlined in this answer. If you have an example program where the high priority threads *don't* spend all their time sleeping, and it's still not behaving as you expect, that would be a different question with a different answer. – caf Oct 02 '15 at 07:45
1

Regarding your ultimate goal of evaluating Linux's real behaviour in comparison to one with the premp-rt patch, the answer is not difficult.

Of course it behaves similarly, it's just that the latencies are higher are wildly variable, but still bounded.

You can write real time software for stock Linux that will work perfectly satisfactorily, provided that your real time response requirements aren't that challenging. If those requirements are to respond within a few milliseconds then you are going to need the premp-rt patches. If your requirements are tighter even than that then you'd need a different OS (VxWorks, etc).

There's some really good advise here and especially here. Linux preempt-rt and real time is not perfect, especially on Intel hardware, but if your requirements aren't that tight then you can do very well.

I can highly recommend downloading and applying preempt-rt yourself. That way you can compile FTRACE into your kernel and then you can play with kernelshark. You can put FTRACE Into a stock kernel too. Kernelshark is extremely useful when developing multi threaded applications, especially when you turn on task plots.

bazza
  • 7,580
  • 15
  • 22
  • thanks for your reply; about real time that will work perfectly in Linux standard: do you have an example that can prove this? So that I can try directly, with two or more threads in competition with different priorities. About the rt patch, I already compiled and installed a rt patch, [this one] (http://jensd.be/589/linux/complile-and-use-a-realtime-kernel-on-centos-7-or-rhel-7) actually, but still I did not see that much difference with standard Linux. My problem is just to have a working example, where I can be sure it's working properly :) – Stef Sep 30 '15 at 06:46