Scope of bool value (threads)

I have managed to fix a problem where an amplifiers mute functionality was not working properly by putting the computer to sleep for .04 seconds after muting:

1
2
3
4
5
6
7
8
9
10
void amplifierOn()
    {
      std::ofstream amp(amplifierGPIO);
      if (amp.is_open())
      {
        amp << "1";
        amp.close();
      }
      usleep(40000);
    }


This function is being called from within the AlsaSoundManager.cpp and here is a snip-it of that script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  void SoundManager::threadUpdateTone()
  {
    using namespace TimeStuff;
    static const int maxDAC = 0x7FFF;

    while (!m_audioSetup.exitUpdateThread)
    {
      m_toneChannel.get();
      
      LibPlatform::Sound::amplifierOn();

      while( snd_pcm_state(m_toneDev) == SND_PCM_STATE_DRAINING)
      {
        threadfunc::sleep(5);
      }


This seems to be working just fine. However, I would like to think of a way to stop a user from calling the function (say from another thread) while it's sleeping. I am not sure if that's even possible (not exactly sure how sleep() works) but if it is I would like to stop it from happening, any ideas?

Maybe I could toggle a bool value inside of the amp function?
Last edited on
The way to stop multiple threads running the same code at the same time is a mutex.

http://www.cplusplus.com/reference/mutex/

Or the pthread equivalent thereof.
Ah I see, well there is some relevent code in the AlsaSoundManager.cpp script:

1
2
3
4
5
6
7
8
9
10
11
12
13
namespace SoundManager
{
  SoundManager::SoundManager(int PortNum)
  : SoundManagerServer(CapSynthesizer | CapMultiChannel | CapVolumeAdjust, PortNum),
    m_toneThread(0),
    m_toneDev(NULL),
    m_audioSetup(),
    m_toneList(),
    m_toneChannel(100),
    m_toneMutex(),
    m_toneMasterVolume(40)
  {
  }


It's being used in various places throughout the script, for instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
 else if (!m_audioSetup.exitUpdateThread)
      {
        bool keepGoing = ( m_toneDev && m_audioSetup.audioBuffer );

        if (!keepGoing)
        {
          m_toneMutex.wait();
          m_toneList.clear();
          m_toneMutex.signal();
        }

        while (keepGoing)
        {


However, I am not sure where the methods such as wait(), clear() or signal() are coming from. They don't seem to be part of the mutex class. Since I am not sure what there doing I would only be guessing when using them. Maybe all I need is something like:

1
2
m_toneMutex.wait()
LibPlatform::Sound::amplifierOn();


I have never messed around with threads before so I will start doing some research.
Last edited on
I believe this situation is already being handled, can someone confirm?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void SoundManager::initiateSoundDevice()
  {
    using namespace TimeStuff;

    if (!m_toneDev || m_audioSetup.threadIsRunning) // If the thread is already running then shut it down and reopen the audio device.
    {
      printf("%10.3f Shut down thread.\n", fToHMS(now()));
      shutDown();

      assert(!m_audioSetup.threadIsRunning);
      if (m_audioSetup.threadIsRunning)
      {
        perror("Unable to shut down audio update thread");
      }
      else
      {
        printf("%10.3f Open sound device.\n", fToHMS(now()));

        for (int i=0 ; audioDeviceName[i] && !m_toneDev ; i++)
        {
          int result = snd_pcm_open(&m_toneDev, audioDeviceName[i], SND_PCM_STREAM_PLAYBACK, 0);
          if (result < 0)
          {
            std::cerr << "Unable to open sound device(" << audioDeviceName[i] << "), tone canceled. Errmsg: " << snd_strerror(result) << std::endl;
            m_toneDev = NULL;
          }
          else
          {
            printf("%10.3f Sound device (%s) open. Handle: %p\n", fToHMS(now()), audioDeviceName[i], m_toneDev);
          }
        }
      }
    }


It would seem to me that initiateSoundDevice is only allowing the device to run one thread at a time. So if initiateSoundDevice was called while the computer was asleep then it would simply, wait for the tone to complete, shut down the thread and re-open another one, right?

Here is shutDown():

1
2
3
4
5
6
7
8
9
10
11
 void SoundManager::shutDown()
  {
    if (m_audioSetup.threadIsRunning)
    {
      std::cout << "SoundManager::shutDown()" << std::endl;
      m_audioSetup.exitUpdateThread = true;
      m_toneChannel.put(0); // Trigger the update thread.
      threadfunc::threadJoin(m_toneThread); // Wait for the thread to exit. The thread will set m_toneDev = NULL on exit.
      std::cout << "SoundManager::shutDown() Done" << std::endl;
    }
  }
Last edited on
Just a comment on terminology, because you're going to cause a lot of confusion if you keep using this term: what usleep() does is tell the kernel not to allocate CPU time for the calling thread for the specified time. In other words, the calling thread (and only that thread) is suspended for a limited time. It doesn't put the entire computer (i.e. all threads and processes) to sleep.

As for your question, if all you want is to not have random code call amplifierOn() just whenever, what you should do is not make that function public. If that's not possible, the correct way would be to lock a mutex inside it:
1
2
3
4
5
6
7
8
9
10
11
12
void amplifierOn(){
    static std::mutex mutex;
    std::lock_guard<std::mutex> lg(mutex);
    //No two threads will be able to go past this point at the same time.

    std::ofstream amp(amplifierGPIO);
    if (amp.is_open()){
        amp << "1";
        amp.close();
    }
    usleep(40000);
}
Ah I see, I wonder if there is something similar I can do for older c++ versions. The above code doesn't compile:

error: #error This file requires compiler and library support for the upcoming ISO C++ standard, C++0x. This support is currently experimental, and must be enabled with the -std=c++0x or -std=gnu++0x compiler options.

One of the many benefits of working on depreciated hardware....

In any case, one of the reasons I thought something odd was happening with usleep() was because I can't seem to get the same functionality by saying something like:
1
2
LibPlatform::Sound::amplifierOn();
threadfunc::sleep(1/25);


as I do with usleep().
Last edited on
pthread with mutex are old enough and work on everything (we used them on both linux and visual studio so same code would work on both).

whatever sleep works for your versions; there are so many nonstandard sleep functions out there...

just use the old library if you don't have the new stuff. See if you can use a new compiler on the old machine (?) as an alternative, that might be better. But Im right there with you, my work compiler on the server is 2012 and they don't want to mess with it. (We don't use c & C++ much and its needed to compile the server side software that is finicky). I was all excited to get a lambda working and then discovered I couldn't use it on the server.
Last edited on
Ah I see what's happening now. Apparently, a long time ago in an office far far away, someone wrote their own threadFunc class... and it doesn't seem to have lock or lock_guard functionality. Also, I did manage to get the native sleep working, I didn't realize it was implemented in msec.
Yes, you can use other mutex implementations, but don't make the mutex object static like I did above. C++11 added thread-safe semantics for static objects that don't exist in previous versions of the language.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void f(){
    static SomeMutex mutex1; //Not safe!
    mutex1.lock();
    //...
    mutex1.unlock();
}

SomeMutex mutex2; //Safe.

void g(){
    mutex2.lock();
    //...
    mutex2.unlock();
}

But do check if there's any way you can get a newer compiler to run on that machine. C++11 added a lot of little conveniences that you really want to take advantage of.
Last edited on
Thanks guys, yeah we don't have lock and unlock. Instead, we have wait and signal:

1
2
3
4
m_toneMutex.wait()
LibPlatform::Sound::amplifierOn();
threadfunc::sleep(40);
m_toneMutex.signal()




I have a different suggestion (unless I missed something while skimming the thread).

It sounds like turning the amp on takes some time to take effect. During that time you can't send the amp other commands. To model that, you could have the Amplifier class keep track of when it can accept the next command. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Amp {
    struct timespec readyTime;  // time when amp can accept next command
    void waitForReady();  // sleep until readyTime;
    void ampOn();
    ...
}

Amp::ampOn()
{
    waitForReady();
    std::ofstream amp(amplifierGPIO);
      if (amp.is_open())
      {
        amp << "1";
        amp.close();
        readyTime = now() + 40000; // update ready time.
      }
}


I suppose something like that could work as well. In actuality, the reason I am adding the delay is to give the amp's SHDN feature enough time to reach ground from being at a high. Now if I cou

EDIT: Oh... it was staring me in the face the whole time. Ton = 45ms in the datasheet lul
Last edited on
Topic archived. No new replies allowed.