- A timer and critical section question
- Posted by Arsalan Ahmad on June 3rd, 2005
Hi,
Just a query suppose I have a data structure which is used by two functions
A() and B() where B is a timer function which is called by setTimeEvent() to
be called after every 3 sec. Their implemenation is as follows.
int A()
{
EnterCriticalSection(&cs);
..... Some code here
LeaveCriticalSection(&cs);
}
int B()
{
EnterCriticalSection(&cs);
.... Some code here
LeaveCriticalSection(&cs);
}
Is it possible that while A() was executing, timer was called and B() was
called but on the very first line of B(), execution is blocked because A()
has not called LeaveCriticalSection(&cs);.
Could anyone please tell me what will be the effect in both the single and
multiple threaded applications.
Thanks,
Arsalan
- Posted by Maxim S. Shatskih on June 3rd, 2005
Yes it is possible
--
Maxim Shatskih, Windows DDK MVP
StorageCraft Corporation
maxim@storagecraft.com
http://www.storagecraft.com
"Arsalan Ahmad" <arsal__@hotmail.com> wrote in message
news:%23Or4xhGaFHA.4020@TK2MSFTNGP09.phx.gbl...
- Posted by JR Lyon on June 3rd, 2005
Use a semaphore. This kind of looks like a classic semaphore problem
"Arsalan Ahmad" <arsal__@hotmail.com> wrote in message
news:%23Or4xhGaFHA.4020@TK2MSFTNGP09.phx.gbl...
- Posted by Steve Maillet \(eMVP\) on June 3rd, 2005
In a single threaded application nothing happens as there is only one thread
and thus no need for the critical sections either. In a multithreaded
implementation it might be possible for A or B to block the execution of
another thread executing the other function. Using critical sections as you
have is the correct approach to solving potential conflicts. You do want to
keep the > .... Some code here
portions as small as possible as the time it takes to execute that code is
the time the other threads are blocked.
--
Steve Maillet
EmbeddedFusion
www.EmbeddedFusion.com
smaillet at EmbeddedFusion dot com
- Posted by TJ on June 4th, 2005
What I would do is set a global variable to toggle at the beginning and
end of the critical section and running it depending on the value. Then
whichever way it is done there should be no problem.
Arsalan Ahmad wrote:
- Posted by Alexander Grigoriev on June 4th, 2005
Global variables and other home-brewn solutions are very bad approach to
synchronization. They just "happen" to work sometimes, but occasionally will
fail to provide the necessary protection. Then the program mysteriously
crashes. You should use proven system-provided synchronization primitives.
"TJ" <someone@somewhere.com> wrote in message
news:QKqdnbMN2PbuhjzfRVn-tw@midco.net...
- Posted by Severian [MVP] on June 4th, 2005
On Fri, 3 Jun 2005 21:37:25 -0700, "Alexander Grigoriev"
<alegr@earthlink.net> wrote:
If I understand correctly, his operations *were* synchronized by
critical sections (usually quite sufficient within a process).
Additionally, atomic operations may often be used -- carefully and
with full understanding -- without other synchronization techniques.
While I agree with you that O/S provided synchronization is best,
techniques more complex than critical sections are rarely required
unless multiple processes are involved.
Often the most difficult part is determining what actually must be
synchronized! I've seen complex synchronization code that fails
occasionally on multi-processor machines because of simple (and not
always obvious) misunderstanding of what must be synchronized.
--
Phillip Crews aka Severian
Microsoft MVP, Windows SDK
Posting email address is real, but please post replies on the newsgroup.
- Posted by Alexander Nickolov on June 6th, 2005
What is setTimeEvent? If it uses the multimedia timers (e.g.
timeSetEvent), you just made your program multi-threaded.
Incidentally, you are not allowed to call EnterCriticalSection
from within a multimedia timer callback. From the docs on
TimeProc:
====
Remarks
Applications should not call any system-defined functions
from inside a callback function, except for PostMessage,
timeGetSystemTime, timeGetTime, timeSetEvent, timeKillEvent,
midiOutShortMsg, midiOutLongMsg, and OutputDebugString.
====
Could be a documentation bug of course, I'd have expected
SetEvent to also be permissible...
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnickolov@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Arsalan Ahmad" <arsal__@hotmail.com> wrote in message
news:%23Or4xhGaFHA.4020@TK2MSFTNGP09.phx.gbl...
- Posted by Ray Trent on June 6th, 2005
One primitive that it would be useful for the OS to have, but doesn't,
is a generic thread-safe one-writer/one-reader circular buffer that
doesn't require synchronization.
I find myself implementing these quite often.
Alexander Grigoriev wrote:
--
.../ray\..
- Posted by Steve Maillet \(eMVP\) on June 6th, 2005
"One primitive that it would be useful for the OS to have, but doesn't,
is a generic thread-safe one-writer/one-reader circular buffer that
doesn't require synchronization."
Have you looked at CreateMsgQueue() and friends?
--
Steve Maillet
EmbeddedFusion
www.EmbeddedFusion.com
smaillet at EmbeddedFusion dot com
- Posted by Ray Trent on June 6th, 2005
Steve Maillet (eMVP) wrote:
Those are a) still synchronized via a synchronization primitive and b)
only on CE, both as far as I can tell from the docs.
Here's a (hopefully self-explanatory) example of a buffer like what I'm
talking about (indexes are unsigned):
Writer:
....
buf[next_write%bufsize].seqno = INVALID_SEQUENCE_NUMBER;
buf[next_write%bufsize].entry = entry;
buf[next_write%bufsize].seqno = next_write++;
....
Reader:
....
// Add here: Ensure or arrange for next_read to be < next_write,
// accounting for wrapping. "!=" may be sufficient for many usages.
tempSeqno = buf[next_read%bufsize].seqno;
if (tempSeqno == INVALID_SEQUENCE_NUMBER) return FALSE;
entry = buf[next_read%bufsize].entry;
if (buf[(next_read++)%bufsize].seqno != tempSeqno) return FALSE;
....
The reader and writer use distinct index variables, so the only thing
that can potentially be non-thread-safe is the buffer itself, and you
cleverly check to see if a collision happened. If it did, then any data
at that spot would be trashed now anyway no matter what you did, so you
haven't lost anything.
BTW, if you can ensure the writer is atomic WRT the reader by some other
mechanism, you can do away with the invalid sequence number and just
write the real one first.
Very slick, and useful in a lot of places. Even works with ISR/DPC
queues on multi-processor machines.
Anyway, that's just an example of what I'm talking about: a useful adhoc
implementation of a thread safe buffer that doesn't use OS primitives.
Assuming it has no bugs, of course <--- Yes, I know this undermines my
argument :-), but it's pretty simple code.
--
.../ray\..
- Posted by Arsalan Ahmad on June 7th, 2005
Hi,
"Alexander Nickolov" <agnickolov@mvps.org> wrote in message
news:e5onbvraFHA.3132@TK2MSFTNGP09.phx.gbl...
So you are saying if I uses multimedia timers then my application is no more
a single-threaded application. How is it possible??
Thanks,
Arsalan
- Posted by Ray Trent on June 7th, 2005
Wow, I've seen a lot of disrespectfully ill-prepared questions on this
group, but this one takes the cake. Try reading the MSDN documentation
for timeSetEvent and TimeProc. It's not like they are long, complicated,
or hard to find.
Arsalan Ahmad wrote:
--
.../ray\..
- Posted by on June 7th, 2005
Through the magic of the API creating a thread itself.....
-Chris
"Arsalan Ahmad" <arsal__@hotmail.com> wrote in message
news:ud2Dgu1aFHA.2876@TK2MSFTNGP09.phx.gbl...
- Posted by Alexander Grigoriev on June 8th, 2005
This is not best implementation.
You can implement thread-safe ring buffer with up to 64K items, using
InterlockedCompareExchange synchronization.
union QueueDesc
{
LONG L;
struct {
USHORT ReadPos;
USHORT NumItems;
};
};
volatile QueueDesc QD = {0};
Write:
QueueDesc qdw;
QueueDesc qdw1;
do {
qdw.L = QD.L;
qdw1.L = qdw.L;
if (qdw.NumItems == MaxNumItems)
{
return FALSE;
}
int index = qdw.ReadPos + qdw.NumItems;
if (index >= MaxNumItems)
{
index -= MaxNumItems;
}
qdw.NumItems++;
array[index] = src;
} while (qdw.L == InterlockedCompareExchange( & QD.L, qdw.L, qdw1.L));
Read:
QueueDesc qdw;
QueueDesc qdw1;
do {
qdw.L = QD.L;
qdw1.L = qdw.L;
if (qdw.NumItems == 0)
{
return FALSE;
}
dst = array[qdw.ReadPos];
qdw.ReadPos ++;
qdw.NumItems--;
if (qdw.ReadPos >= MaxNumItems)
{
qdw.ReadPos = 0;
}
} while (qdw.L == InterlockedCompareExchange( & QD.L, qdw.L, qdw1.L));
BTW, if you can atomically read items, this queue is multi-reader.
"Ray Trent" <ratrent@nospam.nospam> wrote in message
news:O4GaOfuaFHA.3300@TK2MSFTNGP10.phx.gbl...
- Posted by Alexander Nickolov on June 8th, 2005
Not much to add here, but a little clarification. The timer
events come on a multimedia thread, obviously not on the
thread where you called timeSetEvent. Hence your program
is multi-threaded. You can think of it as if timeSetEvent
spins a thread on your behalf (although that's not necessarily
precise by a long shot...).
--
=====================================
Alexander Nickolov
Microsoft MVP [VC], MCSD
email: agnickolov@mvps.org
MVP VC FAQ: http://www.mvps.org/vcfaq
=====================================
"Arsalan Ahmad" <arsal__@hotmail.com> wrote in message
news:ud2Dgu1aFHA.2876@TK2MSFTNGP09.phx.gbl...
- Posted by Arsalan Ahmad on June 9th, 2005
Thanks buddy.
"Alexander Nickolov" <agnickolov@mvps.org> wrote in message
news:%23Zn2tTFbFHA.3328@TK2MSFTNGP09.phx.gbl...