10

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.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
linsek
  • 3,334
  • 9
  • 42
  • 55
  • 3
    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 Jun 29 '12 at 14:30
  • 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 Jun 29 '12 at 14:32
  • 1
    Make sure you avoid this: http://stackoverflow.com/questions/3886171/why-thread-sleep-is-so-cpu-intensive – Alex W Jun 29 '12 at 14:32
  • 3
    @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 Jun 29 '12 at 14:36
  • The queue implementation is what is running. The result is the thread is utilizing ~45% of the CPU time. Which just isn't acceptable. The select() implementation is easy enough to do, but I'm not sure if opening a socket to a thread is the right approach. Someone more experienced than me is probably more equipped to answer that. – linsek Jun 29 '12 at 14:37
  • @RobertHarvey thanks for the link. After reading the bugs with pause() I think I will opt to avoid using the signal system. As my implementation would be exactly what they are saying causes unreliability. I need to suspend until a signal and then resume work. Thanks for the info though. – linsek Jun 29 '12 at 14:46
  • @Tudor yes, we were misunderstanding each other. I follow you now. The conditional would work fine for communication with the manager, but the solution I need to implement also needs to include the timer for polling, not just true/false conditions. How could I implement the timer with `pthread_cond_t`? – linsek Jun 29 '12 at 14:48
  • 2
    @njozwiak: You can check out `pthread_cond_timedwait`: http://pubs.opengroup.org/onlinepubs/009604599/functions/pthread_cond_wait.html – Tudor Jun 29 '12 at 14:55
  • @Tudor thanks, I was not aware of _timedwait. This could be a good solution as it wouldn't require recoding the manager to thread communication. I'll read up on it. – linsek Jun 29 '12 at 15:00
  • How is the timer implemented? Is it external to the worker thread (like another thread or the kernel with `setitimer` or `timer_create`), or does the worker thread merely do its own timekeeping? – pilcrow Jun 29 '12 at 15:16
  • @pilcrow the thread does it's own time keeping. as the manager has a lot to do and it's own timers to perform. it makes sense for the thread to handle it's own timing. – linsek Jun 29 '12 at 15:18
  • @njozwiak: well, there are other correct and efficient approaches that might make sense, too. :) However, extending your queue to support a "timed dequeue" operation via `pthread_cond_timedwait` — in essence [Martin James' answer](http://stackoverflow.com/a/11263859/132382) — does make sense for you. – pilcrow Jun 29 '12 at 15:20

5 Answers5

5

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.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • A couple semaphore answers here so will just respond to them all here. Would using a semaphore as a timer to wake up and check for data be more efficient than blocking with select() and receiving the data over a socket? On the one hand, I am using shared memory instead of recv(), but on the other, if I never receive a request from another application. I'm processing for no reason. – linsek Jun 29 '12 at 14:55
  • As you describe your problem, the fact that your thread doesn't sleep is really significant. This means if you turn it around, your thread is not doing much work compared to that. So if you master this "sleep" problem, what solution you take will not have much impact on the overall work (CPU cycles) that are used by the thread. The differences are then in (1) convenience, what is programmed and maintained the easiest (2) latency. For both I think semaphores win. – Jens Gustedt Jun 29 '12 at 15:07
  • Semaphore waits are blocking. If the wait() is not yet timed out and the semaphore is not yet signaled and its internal count is zero, no CPU is used at all. The sem wait(timeout) will just not return until one condition or the other is satisfied. If the wait times out, the thred will run once. If the semaphore is signaled 15 times by the manager queueing messages, the thread will then run 15 times and process all 15 and no more. – Martin James Jun 29 '12 at 15:07
  • Oh - note well Jens' comment about interrupt wakeups. On faulty OS that support this 'spurious wakeup feature', you need to check :( – Martin James Jun 30 '12 at 04:18
  • @MartinJames, 'spurious wakeups' are not a fault but foreseen as such. This allows to write low level code that reacts on interrupts. – Jens Gustedt Jun 30 '12 at 06:00
  • @JensGustedt Windows drivers are low-level code that reacts on interrupts, Windows semaphores/events/etc allow communication with such code and between threads. Difference - there is never any spurious wakeups, ever. We may have to agree to differ on this, but I regard any such spurious wakeup on a signal that has not been signaled as a serious bug that, though there is a user-space workaround, should have been dealt with in the kernel where any such 'features' should stay. Dijkstra would turn over in his grave.. – Martin James Jun 30 '12 at 13:58
  • @MartinJames, this question is clearly tagged as linux, that is a POSIX system. I don't know much about Windows, and in particular with that respect to semaphores, and would hopefully never will be tempted to rant about an OS which I don't know much. In POSIX `sem_t` is designed on purpose to be usable in signal handlers, this is a low level tool by design. This has nothing to do with "faulty" OS and certainly nothing to do with Dijstra. In all I consider your remark quite inappropriate. – Jens Gustedt Jun 30 '12 at 14:41
  • I take your point, and did not mean to be offensive or inappropriate. OTOH, Windows, RTOS, feeeRTOS, RMX, ctl, OS2, etc et. don't have spurious wakeups. Only U**x varants seem to have this feature. – Martin James Jun 30 '12 at 21:17
4

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.

Duck
  • 26,924
  • 5
  • 64
  • 92
4

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.

Alexandru C.
  • 3,337
  • 1
  • 25
  • 27
3

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.

Martin James
  • 24,453
  • 3
  • 36
  • 60
0

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.

caf
  • 233,326
  • 40
  • 323
  • 462