My Environment: C++ Builder XE4.
I am using Mutex. In the following code, I expect that while Timer1 would acquire mutex, Timer2 process would be skipped. However, Timer2 process was not skipped at all.
What is the problem in the code?
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
String MutexName = L"Project1";
HANDLE HWNDMutex;
void __fastcall TForm1::FormShow(TObject *Sender)
{
HWNDMutex = CreateMutex(NULL, false, MutexName.c_str());
if (HWNDMutex == NULL) {
String msg = L"failed to create mutex";
OutputDebugString(msg.c_str());
}
Timer1->Enabled = false;
Timer1->Interval = 1000; // msec
Timer1->Enabled = true;
Timer2->Enabled = false;
Timer2->Interval = 200; // msec
Timer2->Enabled = true;
}
__fastcall TForm1::~TForm1()
{
CloseHandle(HWNDMutex);
}
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if (WaitForSingleObject(HWNDMutex, INFINITE) == WAIT_TIMEOUT) {
return;
}
if (CHK_update->Checked) {
String msg = L"Timer1 " + Now().FormatString(L"yyyy/mm/dd hh:nn:ss.zzz");
Memo1->Lines->Add(msg);
}
for(int loop=0; loop<10; loop++) {
Application->ProcessMessages();
Sleep(90); // msec
}
ReleaseMutex(HWNDMutex);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
if (WaitForSingleObject(HWNDMutex, INFINITE) == WAIT_TIMEOUT) {
return;
}
if (CHK_update->Checked) {
String msg = L">>>Timer2 " + Now().FormatString(L"yyyy/mm/dd hh:nn:ss.zzz");
Memo1->Lines->Add(msg);
}
ReleaseMutex(HWNDMutex);
}
//---------------------------------------------------------------------------
Result
Timer1 2017/11/08 15:20:39.781
>>>Timer2 2017/11/08 15:20:39.786
>>>Timer2 2017/11/08 15:20:40.058
>>>Timer2 2017/11/08 15:20:40.241
>>>Timer2 2017/11/08 15:20:40.423
>>>Timer2 2017/11/08 15:20:40.603
Timer1 2017/11/08 15:20:40.796
>>>Timer2 2017/11/08 15:20:40.799
>>>Timer2 2017/11/08 15:20:41.071
>>>Timer2 2017/11/08 15:20:41.254
>>>Timer2 2017/11/08 15:20:41.436
>>>Timer2 2017/11/08 15:20:41.619
Timer1 2017/11/08 15:20:41.810
>>>Timer2 2017/11/08 15:20:41.811
>>>Timer2 2017/11/08 15:20:42.083
>>>Timer2 2017/11/08 15:20:42.265
>>>Timer2 2017/11/08 15:20:42.448
>>>Timer2 2017/11/08 15:20:42.633
I tried using TMutex with acquire() and release(), but it did not work either.
A mutex has a thread affinity and thus is re-entrant:
TTimer
is a message-based timer. You have two timers running in the same thread. Which means theirOnTimer
events are serialized by default in relation to each other. Only one event can be running at a time (unless you do something stupid like callApplication->ProcessMessages()
, which is a re-entrant nightmare).Timer2
will trigger first (4-5 times, actually), acquiring and releasing the mutex lock each time, beforeTimer1
triggers. ThenTimer1
triggers, acquires the lock, runs a loop to pump the main UI message queue, thus allowingTimer2
to trigger again (multiple times) whileTimer1Timer()
is still running.Timer2
will re-acquire and release the same lock that the UI thread already has, soWaitForSingleObject()
exits withWAIT_OBJECT_0
immediately. Then the loop ends andTimer1
releases the lock.Your mutex is useless in this code. A mutex is meant for inter-thread synchronization, but you have no worker threads in this code! You have a single thread synchronizing against itself, which is redundant, and exactly the kind of deadlock-causing situation that many synchronization objects avoid by supporting re-entry.
A critical section also has a thread affinity and is re-entrant, so that is not going to help you, either:
However, a semaphore would work for what you are attempting, as it does not have a thread affinity:
If you switch to a semaphore, your code as shown would deadlock itself as soon as
Application->ProcessMessages()
is called and the semaphore counter drops to 0, because of your use ofINFINITE
timeouts. So use smaller timeouts to prevent that.Try this:
On a side note: beware of giving a kernel-based synchronization object a name. That allows other processes to access it and mess around with its state behind your back. Don't name objects that you don't intend to share across process boundaries! Mutexes and semaphores are namable objects.