summaryrefslogtreecommitdiff
path: root/NOTES
blob: 6f41578e3f59178dee07397aefbc8cb6bd538143 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Notes from the second Tulip/Twisted meet-up
===========================================

Rackspace, 12/11/2012
Glyph, Brian Warner, David Reid, Duncan McGreggor, others

Flow control
------------

- Pause/resume on transport manages data_received.

- There's also an API to tell the transport whom to pause when the
  write calls are overwhelming it: IConsumer.registerProducer().

- There's also something called pipes but it's built on top of the
  old interface.

- Twisted has variations on the basic flow control that I should
  ignore.

Half_close
----------

- This sends an EOF after writing some stuff.

- Can't write any more.

- Problem with TLS is known (the RFC sadly specifies this behavior).

- It must be dynamimcally discoverable whether the transport supports
  half_close, since the protocol may have to do something different to
  make up for its missing (e.g. use chunked encoding).  Twisted uses
  an interface check for this and also hasattr(trans, 'halfClose')
  but a flag (or flag method) is fine too.

Constructing transport and protocol
-----------------------------------

- There are good reasons for passing a function to the transport
  construction helper that creates the protocol.  (You need these
  anyway for server-side protocols.)  The sequence of events is
  something like

  . open socket
  . create transport (pass it a socket?)
  . create protocol (pass it nothing)
  . proto.make_connection(transport); this does:
    . self.transport = transport
    . self.connection_made(transport)
  
  But it seems okay to skip make_connection and setting .transport.
  Note that make_connection() is a concrete method on the Protocol
  implementation base class, while connection_made() is an abstract
  method on IProtocol.

Event Loop
----------

- We discussed the sequence of actions in the event loop.  I think in the
  end we're fine with what Tulip currently does.  There are two choices:

  Tulip:
  . run ready callbacks until there aren't any left
  . poll, adding more callbacks to the ready list
  . add now-ready delayed callbacks to the ready list
  . go to top

  Tornado:
  . run all currently ready callbacks (but not new ones added during this)
  . (the rest is the same)

  The difference is that in the Tulip version, CPU bound callbacks
  that keep adding more to the queue will starve I/O (and yielding to
  other tasks won't actually cause I/O to happen unless you do
  e.g. sleep(0.001)).  OTOH this may be good because it means there's
  less overhead if you frequently split operations in two.

- I think Twisted does it Tornado style (in a convoluted way :-), but
  it may not matter, and it's important to leave this vague so
  implementations can do what's best for their platform.  (E.g. if the
  event loop is built into the OS there are different trade-offs.)

System call cost
----------------

- System calls on MacOS are expensive, on Linux they are cheap.

- Optimal buffer size ~16K.

- Try joining small buffer pieces together, but expect to be tuning
  this later.

Futures
-------

- Futures are the most robust API for async stuff, you can check
  errors etc.  So let's do this.

- Just don't implement wait().

- For the basics, however, (recv/send, mostly), don't use Futures but use
  basic callbacks, transport/protocol style.

- make_connection() (by any name) can return a Future, it makes it
  easier to check for errors.

- This means revisiting the Tulip proactor branch (IOCP).

- The semantics of add_done_callback() are fuzzy about in which thread
  the callback will be called.  (It may be the current thread or
  another one.)  We don't like that.  But always inserting a
  call_soon() indirection may be expensive?  Glyph suggested changing
  the add_done_callback() method name to something else to indicate
  the changed promise.

- Separately, I've been thinking about having two versions of
  call_soon() -- a more heavy-weight one to be called from other
  threads that also writes a byte to the self-pipe.

Signals
-------

- There was a side conversation about signals.  A signal handler is
  similar to another thread, so probably should use (the heavy-weight
  version of) call_soon() to schedule the real callback and not do
  anything else.

- Glyph vaguely recalled some trickiness with the self-pipe.  We
  should be able to fix this afterwards if necessary, it shouldn't
  affect the API design.