I am developing a multi-threaded application. One of the threads is dedicated to handling an alarm system, which plays a certain alarm (sequence of specific tones) according to the alarm case. There's also an option to mute the alarm, by raising a flag from another thread. Whenever an alarm is muted, I want my program to immediately stop the alarm and not go through all the sequence. Below is a toy example of how my code is structured:
static int mute = 0;
static int current_alarm = 0;
void play_alarm_2 (void)
{
play_g_tone();
sleep(30);
play_a_tone();
sleep(20);
. . .
}
void play_alarm_1 (void)
{
play_c_tone();
sleep(10);
play_f_tone();
sleep(20);
. . .
}
void set_mute_status (int mute_status)
{
mute = mute_status;
}
void set_current_alarm (int alarm)
{
current_alarm = alarm;
}
int main()
{
for(;;)
{
switch(current_alarm)
case 1:
play_alarm_1();
case 2:
play_alarm_2();
. . .
}
}
Other threads set the alarm case and the muting option. Now for example, if the alarm thread is currently inside play_alarm_2 and is playing the g_tone, if the user suddenly mutes the alarm, I would like to exit the function play_alarm_2 (the g_tone can finish to play, it doesnt have to cut it short necessarly). A stupid way to do it would be to add an every other line:
void play_alarm_2 (void)
{
if (mute)
return
play_g_tone();
if (mute)
return
sleep(30);
if (mute)
return
play_a_tone();
if (mute)
return
sleep(20);
. . .
}
Which again looks a bit stupid but does it solves my problem. I could also set up a macro maybe for it. But my question is, is there a more proper way to do this? Probably there is a solution that includes introducing another thread, but I would avoid it If i can. If really there's no way to do this without another thread, what would be the proper way to implement this?
P.S: This code will go on an embedded system and I am using ThreadX.
A sketch of "how to do this":
#define N 6 // 0-5 steps
for( int i = 0; ; i = (i+1) % N ) { // 0-1-2-3-4-5-0-1-2...
switch( i ) {
case 0: play_a_tone(); break;
case 2: play_g_tone(); break;
case 4: play_c_tone(); break;
case 1:
case 3:
sleep(30);
break;
case 5:
sleep(200);
break;
}
if( mute ) return;
}
If the only thing happening in the function is this loop, you could even:
for( int i = 0; !mute; i = (i+1) % N ) {
and wouldn't need the extra if()
at the bottom of the loop...
UPDATE:
Happened across this and wanted to post an 'improvement' that clearly separates the data from the functionality:
struct {
void (*tone)();
int dur;
} sqnc[] = {
{ play_a_tone, 30 }, // repeatedly played in sequence
{ play_g_tone, 30 },
{ play_c_tone, 200 },
{ play_f_tone, 30 },
};
const int N = sizeof sqnc/sizeof *sqnc;
for( int i = 0; !mute; i = (i+1) % N ) {
sqnc[i].tone(); if( !mute ) sleep( sqnc[i].dur );
}
This suggests it could be made even simpler if the separate play_X_tone()
functions were combined into a single function that takes and uses a parameter. Haven't seen that code, so this is just an observation.
(Just put Donna Summer's "I Feel Love" on the stereo... Multiple threads?? You're on your way to re-inventing the 'digital sequencer'. :-)
)