c++ - EnterCriticalSection Deadlock -


having appears dead-lock situation multi-threaded logging application.

little background:

my main application has 4-6 threads running. main thread responsible monitoring health of various things i'm doing, updating guis, etc... have transmit thread , receive thread. transmit , receive threads talk physical hardware. need debug data transmit , receive threads seeing; i.e. print console without interrupting them due time critical nature of data. data, way, on usb bus.

due threading nature of application, want create debug console can send messages other threads. debug consule runs low priority thread , implements ring buffer such when print debug console, message stored ring buffer , sets , event. debug console's thread sits waitingonsingleobject events in bound messages come in. when event detected, console thread updates gui display message. simple eh? printing calls , console thread use critical section control access.

note: can adjust ring buffer size if see dropping messages (at least that's idea).

in test application, console works if call print method via mouse clicks. have button can press send messages console , works. however, if put sort of load (many calls print method), dead-locks. when trace dead-lock, ide's debugger traces entercriticalsection , sits there.

note: if remove lock/unlock calls , use enter/leavecriticalsection (see code) work still find myself in dead-lock situation. rule out deadlocks stack push/pops, call enter/leavecriticalsection directly did not solve issue.... what's going on here?

here 1 print statement, allows me pass in simple int display console.

void tgdb::print(int i) {     //lock();     entercriticalsection(&cs);      if( !suppressoutput )     {         //swprintf( msgrec->msg, l"%d", i);         sprintf( msgrec->msg, "%d", i);         mbuffer->putmsg(msgrec, 1);     }      setevent( m_hevent );     leavecriticalsection(&cs);     //unlock(); }  // lock/unlock methods void tgdb::lock(void) {     entercriticalsection(&cs); }  bool tgdb::trylock(void) {     return( tryentercriticalsection(&cs) ); }  void tgdb::unlock(void) {         leavecriticalsection(&cs); }  // how implemented console's thread routines  dword winapi tgdb::consolethread(pvoid pa) { dword rval;           tgdb *g = (tgdb *)pa;         return( g->processmessages() ); }  dword tgdb::processmessages() { dword rval; bool brval; int msgcnt;          {         rval = waitformultipleobjects(1, &m_hevent, true, iwaittime);          switch(rval)         {             case wait_object_0:                  entercriticalsection(&cs);                 //lock();                  if( keeprunning )                 {                     info->caption = "rx";                     info->refresh();                     msgcnt = mbuffer->getmsgcount();                      for(int i=0; i<msgcnt; i++)                     {                         mbuffer->getmsg( msgrec, 1);                         log->lines->add(msgrec->msg);                     }                 }                  brval = keeprunning;                 resetevent( m_hevent );                 leavecriticalsection(&cs);                 //unlock();              break;              case wait_timeout:                 entercriticalsection(&cs);                 //lock();                 info->caption = "idle";                 info->refresh();                 brval = keeprunning;                 resetevent( m_hevent );                 leavecriticalsection(&cs);                 //unlock();             break;              case wait_failed:                 entercriticalsection(&cs);                 //lock();                 brval = false;                 info->caption = "error";                 info->refresh();                 aline.sprintf("console error: [%d]", getlasterror() );                 log->lines->add(aline);                 aline = "";                 leavecriticalsection(&cs);                 //unlock();             break;         }      }while( brval );      return( rval ); } 

mytest1 , mytest2 2 test functions call in response button press. mytest1 never causes problem no matter how fast click button. mytest2 dead locks everytime.

// no dead lock void ttest::mytest1() {     if(gdb)     {         // else where: gdb = new tgdb;         gdb->print(++i);     } }   // causes dead lock void ttest::mytest2() {     if(gdb)     {         // else where: gdb = new tgdb;         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);         gdb->print(++i);     } } 

update: found bug in ring buffer implementation. under heavy load, when buffer wrapped, didn't detect full buffer buffer not returning. i'm pretty sure issue resolved. once fixed ring buffer issue, performance got better. however, if decrease iwaittime, dead lock (or freeze issue) returns.

so after further tests heavier load appears deadlock not gone. under super heavy load continue deadlock or @ least app freezes no near use since fixed ring buffer problem. if double number of print calls in mytest2 can lock every time....

also, updated code reflected above. know make sure set & reset event calls inside critical section calls.

with options closed up, ask questions "info" object. window, window parented to, , thread created on?

if info, or parent window, created on other thread, following situation might occur:

the console thread inside critical section, processing message. main thread calls print() , blocks on critical section waiting console thread release lock. console thread calls function on info (caption), results in system sending message (wm_settext) window. sendmessage blocks because target thread not in message alertable state (isn't blocked on call getmessage/waitmessage/msgwaitformultipleobjects).

now have deadlock.

this kind of #$(%^ can happen whenever mix blocking routines interacts windows. appropriate blocking function use on gui thread msgwaitformultipleobjects otherwise sendmessage calls windows hosted on thread can deadlock.

avoiding involves 2 possible approaches:

  • never doing gui interaction in worker threads. use postmessage dispatch non blocking ui update commands ui thread, or
  • use kernel event objects + msgwaitformultipleobjects (on gui thread) ensure when blocking on resource, still dispatching messages.

Comments

Popular posts from this blog

apache - Add omitted ? to URLs -

redirect - bbPress Forum - rewrite to wwww.mysite prohibits login -

php - How can I stop spam on my custom forum/blog? -