10
votes

Alright, so this question isn't exactly about thread management... well, sort of. I am looking for different solutions to this configuration. I have a few ideas, but am looking for any solutions that could satisfy the problem. And will weigh the pros and cons to implement the best one.

Here is the situation.

I have a manager application that will spawn a threads. This thread will continuously run and handle serial communication with boards that are connected to the system via USB. The manager application facilitates communication between other applications running on the system and this thread. The thread needs to really perform two things:

  1. Poll the boards for sample data via serial on a variable timer.. usually about once a minute (the serial bus is rather slow, baud is 4800. I can't control this)
  2. Facilitate communication with the manager application. (i.e. other applications will request sample data, the manager forwards the request to the thread. the thread performs the operation and returns the data)

My initial design was a simple one and works. I use a queue and a mutex for manager to thread communication. So the logic of the thread is as follows:

  1. Initialization
  2. While we have not received a shutdown command from the manager
  3. If our timer is up, poll the board for data
  4. Otherwise, check to see if we have a message posted by the manager to the queue. if so, process it

The problem is I did not consider CPU utilization. 99.9% of the time my thread is processing nothing and just sucking up power. I need to implement a way to sleep this thread until it has work to do. So a couple ideas:

Use select() to block. This can block based on the timer I need to use, and I could change the queue messaging implementation to socket messaging. So instead, the thread would open a client socket to the manager and the manager would pass the messages over the socket to the thread. Then select() would sleep until there was activity on the fd or my timer was up.

Pro: Exactly the functionality I need.

Con: Aren't sockets a bit heavy processing for communication to a thread where you share memory already?

Use a signal system. (Someone more knowledgeable in Linux can pipe up here with an implementation example... I'm not sure exactly how to do it.) But the thread could sleep for the duration of the timer, and wake up to process if a signal was received from the manager.

Pro: Maintains current implementation using shared memory

Con: Not sure how to implement. Is there a function like select() that works with signals instead of fds?

Potentially a mutex. I could block until a mutex was posted to by the manager.

Pro: Still sharing memory

Con: might need to move timer processing to the manager and that really isn't an option as it has other timers and critical work to perform.

Please recommend and feel free to critique. I am open to any efficient options. Please note though this is running on an embedded system, so resources usage is critical.

5
Why not use a conditional variable when you have to wait for an event? The thread is effectively put to sleep until the variable is signaled by another thread.Tudor
I absolutely could, but the point is I don't want to be processing anything. I don't want to repeatedly check the value of a variable that is going to change once every minute. That's a lot of overhead. I want the thread to sleep so that it gives up CPU time.linsek
Make sure you avoid this: stackoverflow.com/questions/3886171/…Alex W
@njozwiak: Wait I don't think we are talking about the same conditional variable. I meant pthread_cond_t, which suspends the thread when you call pthread_cond_wait, not a busy spin. Or am I misunderstanding something?Tudor
@njozwiak: You can check out pthread_cond_timedwait: pubs.opengroup.org/onlinepubs/009604599/functions/…Tudor

5 Answers

5
votes

The classical tool to handle such situations are semaphores and not mutexes or condition variables. Think of them as tokens passed from the manager to the thread.

The thread could use sem_timedwait to be sure to wake up once in a while to check for data.

Beware to capture the error returns of sem_ functions well, they are interruptible. So you may have a bit more wake ups than you would think.

4
votes

Try something like this, using semaphores:

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>

static sem_t s_sem;
static int iAlive = 1;

void* thFunc(void *param)
{
    printf("%s : ENTER \n", __FUNCTION__);
    while(iAlive)
    {
        printf("%s : waiting \n", __FUNCTION__);
        sem_wait(&s_sem);

        printf("%s : got a signal - doing something \n", __FUNCTION__);
        sleep(1);
    }

    printf("%s : EXIT \n", __FUNCTION__);
    return 0;
}

int main()
{
    pthread_t thread;
    sem_init(&s_sem, 0, 0);

    if(0 != pthread_create(&thread, NULL, thFunc, NULL))
    {
        printf("%s : pthread_create FAILED \n", __FUNCTION__);
        return -1;
    }

    while ( getchar() != 'q' )
    {
        printf("%s : sending signal \n", __FUNCTION__);
        sem_post(&s_sem);
    }

    iAlive = 0;
    sem_post(&s_sem);
    pthread_join(thread, NULL);
    sem_destroy(&s_sem);

    return 0;
}

You can replace sem_wait with sem_timedwait if you need a timeout.

4
votes

Switch to POSIX message queues instead of your own. mq_timedreceive will return if the manager posts a request. If it times out, you have to do your timer polling. The synchonization and blocking comes already packaged.

3
votes

Have each thread wait on an input producer-consumer queue with a timeout. If the queue wait times out, poll the serial link, otherwise process the command received on the queue. To form a suitable queue from scratch, you need an actual queue, (which you already have), a mutex to protect the queue pointers/indexes, (which you already have), and a semaphore, initialized to 0, with a wait(timeout) function. To send the thread a request, lock the mutex, push the request, unlock the mutex, signal the semaphore. In the thread, wait on the semaphore, if wait returns with no timeout, lock the mutex, pop the request, (for there will always be one), unlock the mutex and process the received request. If the sema wait returns with a timeout, poll the serial link. When done, loop around to wait on the semaphore again.

To vary the timeout, send the thread a message with the command 'EchangeWaitInterval', (say:), and the new timeout interval to use for subsequent waits.

0
votes

The classic pthreads approach to this would be to have your thread block in pthread_cond_wait() until the manager thread puts a message in the queue and signals the condition variable. In this case, to wake up in a timely manner to poll the serial devices, use pthread_cond_timedwait() instead.