3

I have a class that inherits from twisted.internet.protocol.DatagramProtocol class. In my startProtocol() implementation I call startWriting(), so that socket gets notified each time I can write to it without blocking. Two questions:

  1. Which method twisted will call once socket will become writable?
  2. How should I call startWriting() method, if it needs to be called on particular time-intervals to limit the outgoing UDP bandwidth to particular Datagrams/Second amount?
jathanism
  • 33,067
  • 9
  • 68
  • 86
user389238
  • 1,656
  • 3
  • 19
  • 40

1 Answers1

2

Oops. I think I may have answered your question in another thread and lead you to believe that UDP flow control support in Twisted is a little more robust than it actually is. Nevertheless, you can get what you need to do done...

1. Which method will Twisted call once socket will become writable?

Unfortunately, UDP protocols in Twisted do not get monitored for writability, on the premise that UDP can always fail and so it should never raise EWOULDBLOCK. (Except actually, it does sometimes, and this is a bug in Twisted which I just re-discovered while answering this question. This only happens if Twisted is sending UDP at faster than local wire speed, which requires a very fast application and a very slow network.)

As a workaround, your application can simply catch EWOULDBLOCK. For any other protocol, this type of workaround might constitute a serious problem, but in the case of UDP you already have to be prepared to lose any outgoing packets, so you need an in-band control flow mechanism anyway.

Helping us get that bug through the review process is always an option too.

If you want to get really fancy, you can write your own alternative to udp.Port (by implementing IFileDescriptor yourself) rather than writing a UDP protocol, and override doRead and doWrite (which are called when the underlying socket is readable and writable respectively). This will give you perfect write-level flow control, but probably isn't necessary since, again, UDP will just drop your packets sometimes, and on networks without the ability to properly process "ICMP source quench" messages (which dumb firewalls configured to block ICMP will just block), dropped packets are your only source of flow control information. I'm not saying that you shouldn't fix this bug in Twisted for real, but this fact of life in the UDP world is the likely reason that nobody has bothered to do so yet.

2. How should I call startWriting() method, if it needs to be called on particular time-intervals to limit the outgoing UDP bandwidth to particular Datagrams/Second amount?

Due to the limitations described in part 1 of this answer, UDP transports do not have a useful startWriting method.

However, startWriting/stopWriting isn't really the right way to time-limit your outgoing UDP bandwidth anyway.

Simply call self.transport.write(...) at the appropriate times, after scheduling said calls with via an appropriate scheduling mechanism. LoopingCall, for example, was designed to invoke UDP sends for RTP media streaming at the appropriate interval for transmitting a sound sample. But you can also just calculate your own delay and use callLater directly. In any case, you likely will need to keep around some kind of queue mechanism in the event that you need to do any re-transmissions of outgoing data queued for transport over UDP, so just pop

If you need to do inbound flow-control, UDP transports still support that quite nicely, with stopReading and startReading.

Hope this answer was helpful, and sorry if I previously mislead you about Twisted's capabilities in this area!

Glyph
  • 31,152
  • 11
  • 87
  • 129
  • 1
    Thanks for the answer. I implemented UDP flow control with LoopingCall. The issue with Looping Call is that it can't push more than 300KBps when UDP packets are small (50 Bytes). During this test CPU is not maxed out. Maybe I should try to do write() in a loop when this LoopingCall callback is called... – user389238 Oct 28 '11 at 16:52
  • You're saying that `LoopingCall` with a 0 timeout only pushes ~6000 UDP packets per second on your hardware, but does *not* max out your CPU? What about a naive `callLater` loop? – Glyph Oct 28 '11 at 21:14
  • 1
    I just implemented UDP sending in a tight infinite loop and was able to max out CPU while still achieving ONLY 8MBps with 50 byte packets. Still not a lot, but 20x better than with LoopingCall. Most likely the big bottleneck here is Python. It would be nice if I could send multiple datagrams with a single sendto() call. – user389238 Oct 29 '11 at 01:56
  • For what it's worth, if you are not maxing out CPU, then the bottleneck cannot be "python", unless you are thrashing the GIL or doing some other I/O that would cause your process to stall. – Glyph Aug 07 '12 at 03:29