MCPcopy Create free account
hub / github.com/amule-project/amule / WriteDoneAndQueueEmpty

Method WriteDoneAndQueueEmpty

src/ExternalConn.cpp:343–402  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

341}
342
343void CECServerSocket::WriteDoneAndQueueEmpty()
344{
345 if (!HaveNotificationSupport() || m_conn_state != CONN_ESTABLISHED) {
346 //printf("[EC] %p: WriteDoneAndQueueEmpty but notification disabled\n", this);
347 return;
348 }
349
350 // CECSocket::OnOutput drains the per-socket output queue, then calls
351 // WriteDoneAndQueueEmpty to pull the next notification packet. The
352 // chain runs synchronously on the main thread:
353 //
354 // WriteDoneAndQueueEmpty -> SendPacket -> WritePacket + OnOutput
355 // -> OnOutput drains queue -> WriteDoneAndQueueEmpty -> ...
356 //
357 // On a busy amuled (many peers / files generating notifications) the
358 // ECNotifier always has the next packet ready, so the recursion never
359 // bottoms out. The main thread stays inside this chain processing the
360 // notifier feed and never yields back to the wx event loop. That
361 // starves every other event -- including LibSocketLost from a
362 // half-closed EC peer, which is what CECServerSocket::OnLost needs
363 // to fire so it can tear the dead socket down.
364 //
365 // In the wedged-amuleweb scenario reported in #666, the peer is in
366 // kernel CLOSE-WAIT, writes silently succeed against the dead
367 // kernel buffer, and amuled spins indefinitely flushing the
368 // notifier to nowhere -- amulegui can't connect because the main
369 // thread is permanently occupied.
370 //
371 // Cap the dispatch depth so the chain returns to the event loop
372 // every MAX_DEPTH packets. The pending asio LibSocketSend
373 // completions (or LibSocketLost, if the peer has gone away) get
374 // processed in between; on a healthy peer the loop simply resumes
375 // when OnSend re-enters via the next completion.
376 static const int MAX_DEPTH = 8;
377 if (m_notification_dispatch_depth >= MAX_DEPTH) {
378 return;
379 }
380
381 // ECNotifier::GetNextPacket returns a fresh new CECPacket(...) and
382 // the caller owns it; SendPacket(const CECPacket*) only serialises
383 // it into the per-socket output queue and never deletes. Hand the
384 // raw pointer to a smart pointer so the CECPacket (and its CECTag
385 // tree) get freed at scope exit instead of leaking on every
386 // notification dispatch. Pre-fix, the EC notification path was
387 // the dominant retained-bytes leak on long-running amuled with
388 // connected amulegui / amuleweb peers (#765).
389 CSmartPtr<CECPacket> packet(m_ec_notifier->GetNextPacket(this));
390 if (!packet) {
391 return;
392 }
393
394 m_notification_dispatch_depth++;
395 try {
396 SendPacket(packet.get());
397 } catch (...) {
398 m_notification_dispatch_depth--;
399 throw;
400 }

Callers

nothing calls this directly

Calls 2

GetNextPacketMethod · 0.80
getMethod · 0.45

Tested by

no test coverage detected