Event handling for queues?

Event handling for queues?

Post by Simon Cook » Tue, 10 Sep 2002 10:07:38



Hi there,

I just need to check something:

If I start waiting on a manual-reset event, and the event was set a
while back, has not yet been reset, and another thread (or the same one)
has already been waiting on it, will the wait act as if the event was
signalled? Or do I have to reset it, and set it again?

I'm seeing some very odd behavior in a networking app I'm working on,
and this looks like the most likely cause.

eg:

Feeder Thread:
Obtains critical section on queue object
Adds item to queue.
If queue was empty, SetEvent is called on queue's event handle.
Releases critical section on queue.

Eater Thread:

loop {

Waits for queue object's event handle, or global "Cancel operations"
event handle to be set.

If cancel, abort & shutdown.

Obtain critical section on queue object.
Remove item from queue.
If queue is now empty, queue's event handle is now reset.
Release critical section on queue.

Quote:}

I'm worried that after my first go around the Eater Thread's loop, I'm
getting stuck. I've done my own testing, and the behavior I'm expecting
with my code seems to be the correct behavior, but I'd just like to
double check just in case I'm missing something.

Thanks!

Si

 
 
 

Event handling for queues?

Post by Gary Chanso » Tue, 10 Sep 2002 17:18:04



Quote:> Hi there,

> I just need to check something:

> If I start waiting on a manual-reset event, and the event was set a
> while back, has not yet been reset, and another thread (or the same one)
> has already been waiting on it, will the wait act as if the event was
> signalled? Or do I have to reset it, and set it again?

No, you don't have to reset and set it.  "Manual reset" means that it stays
signaled until reset.

Quote:> I'm seeing some very odd behavior in a networking app I'm working on,
> and this looks like the most likely cause.

> eg:

> Feeder Thread:
> Obtains critical section on queue object
> Adds item to queue.
> If queue was empty, SetEvent is called on queue's event handle.
> Releases critical section on queue.

> Eater Thread:

> loop {

> Waits for queue object's event handle, or global "Cancel operations"
> event handle to be set.

> If cancel, abort & shutdown.

> Obtain critical section on queue object.
> Remove item from queue.
> If queue is now empty, queue's event handle is now reset.
> Release critical section on queue.

> }

> I'm worried that after my first go around the Eater Thread's loop, I'm
> getting stuck. I've done my own testing, and the behavior I'm expecting
> with my code seems to be the correct behavior, but I'd just like to
> double check just in case I'm missing something.

    I don't see a flaw in your logic.  How is the wait implemented?

--

-Gary Chanson (MVP for Windows SDK)
-Software Consultant (Embedded systems and Real Time Controls)

-Abolish public schools

 
 
 

Event handling for queues?

Post by Simon Cook » Tue, 10 Sep 2002 18:48:17




>> If I start waiting on a manual-reset event, and the event was set a
>> while back, has not yet been reset, and another thread (or the same
>> one) has already been waiting on it, will the wait act as if the
>> event was signalled? Or do I have to reset it, and set it again?

> No, you don't have to reset and set it.  "Manual reset" means that it
> stays signaled until reset.

Ahhh... ok. I came to that conclusion after writing some of my own
tests, but I wanted to make sure that something wasn't going to appear
and bite me in the ass later. All of the documentation I can find uses
the phrasing "the thread is resumed when the event *becomes* signalled",
rather than when it *is* signalled, which made me wonder if it was edge
triggered, or level triggered.

Quote:>> I'm seeing some very odd behavior in a networking app I'm working on,
>> and this looks like the most likely cause.

>> eg:

>> Feeder Thread:
>> Obtains critical section on queue object
>> Adds item to queue.
>> If queue was empty, SetEvent is called on queue's event handle.
>> Releases critical section on queue.

>> Eater Thread:

>> loop {

>> Waits for queue object's event handle, or global "Cancel operations"
>> event handle to be set.

>> If cancel, abort & shutdown.

>> Obtain critical section on queue object.
>> Remove item from queue.
>> If queue is now empty, queue's event handle is now reset.
>> Release critical section on queue.

>> }

>> I'm worried that after my first go around the Eater Thread's loop,
>> I'm getting stuck. I've done my own testing, and the behavior I'm
>> expecting with my code seems to be the correct behavior, but I'd
>> just like to double check just in case I'm missing something.

>     I don't see a flaw in your logic.  How is the wait implemented?

 bool WaitForDataOrCancel()
 {
  HANDLE events[2];
  events[0] = cancelOps;
  events[1] = queue.dataAvailable;

  return (::WaitForMultipleObjects(2, events, FALSE, INFINITE) ==
WAIT_OBJECT_0 + 1);
 }

... returns true if data is available in the queue, false if everything
has been aborted. Otherwise it just sits and waits.

The source for the queue class follows (Synchronizable is an RIIA-based
wrapper around a critical section, Synchronize is another class which
locks a passed in reference to a Synchronizable object on construction,
and unlocks it on destruction; Event is another RIIA wrapper, this time
around an event handle -- and by default, Create() creates an anonymous
manual-reset event, initialized as non-signalled).

class PacketQueue : public Synchronizable
{
public:
 class PACKET {
 public:
  BYTE* pData;
  size_t len;

  PACKET() : pData(NULL), len(0)
  {
  }

  void Delete()
  {
   if (pData != NULL)
   {
    delete[] pData;
    pData = NULL;
   }
  }

 };

private:

 deque<PACKET> queue;

 static void DeletePacket(PACKET& p);

public:

 Event dataAvailable;

 PacketQueue()
 {
  dataAvailable.Create();
 }

 ~PacketQueue()
 {
  if (!queue.empty()) for_each(queue.begin(), queue.end(),
DeletePacket);
 }

 size_t GetCount()
 {
  Synchronize to(*this);
  return queue.size();
 }

 void Clear()
 {
  Synchronize to(*this);
  dataAvailable.ResetEvent();

  if (!queue.empty()) {
   for_each(queue.begin(), queue.end(), DeletePacket);
  }

  queue.clear();
 }

 void Add(BYTE* pData, size_t len)
 {
  Synchronize to(*this);

  PACKET p;
  p.pData = pData;
  p.len = len;

  queue.push_back(p);

  dataAvailable.SetEvent();
 }

 bool IsEmpty()
 {
  Synchronize to(*this);
  return queue.empty();
 }

 bool Get(PACKET& packet)
 {
  Synchronize to(*this);
  if (queue.empty()) return false;

  if (queue.size() == 1)
  {
   dataAvailable.ResetEvent();
  }

  packet = queue.front();
  queue.pop_front();

  return true;
 }

Quote:};

----------------------------------------------

To be honest, I'm kind of grasping at straws here. I don't believe my
problem is in the networking code -- I've got the nagle algorithm turned
off, so data packets should be going out over the sockets just as fast
as I can generate them -- but I'm seeing a very very strange case where
the queues will stall, and only new data entering into them will force
more data out. (And the queues shouldn't stall; the client and server
are on dedicated machines with a cross-over cable between them).

It's kind of freaky :)

Thanks Gary for all your help -- if nothing else, I can at least rule
out one worry I had :)

Si