summaryrefslogtreecommitdiff
path: root/TODO
blob: 91007e27c0678c5a7452f078cfdb10a799ca4f98 (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
131
132
133
134
135
136
137
138
New, simpler design:

There is an 'Engine' which is both Executor and Mainloop. It uses an
Epoll object to handle the polling. This epoll object does not do any
locking by itself.

Contexts can be created; they are like jobqueues, except they also
allow you to add fd's and idles and timeouts. They can do that since
they have access to the engine. These are completely unlocked; if you
want to access a context from more than one thread, you must lock them
yourself. It is guaranteed that only one callback will be active for a
context at the same time. From within that callback it is then safe to
remove other callbacks etc. In general such a context can act pretty
much like one thread.

A web server will consist of a listening context, which will create
other contexts as necessary and add the fd's to them with the
appropriate callback. Or maybe just call back with the new fd. I guess
there should be a generic listener object that will create its own
context and call back. Then an http module can just create a listener
and do whatever it wants in response.

One possible problem. The thread that creates a context and adds the
fd not necessarily the one that will execute the first callback. Can
these interfere? Maybe the solution is just that the creating thread
should not do anything with the context after it adds the fd.  A
better idea may be to just have a context_dispatch() function that you
must call after creating the context. Until this is called, nothing
the context is not active.

Beginning of this new design is implemented and can be built with
build2.sh.



Old design (referenced above)

Highlevel design for a web server:

        - Executor
                takes callbacks and executes them, possibly in parallell
                or out of order. The callbacks are likely to be called in
                a different thread than the one that queued them.

		Should support both push_back() and push_front(). Push_front()
		is needed to do parallel requests with good latency. If 
		a request can be parallellized, the parallel tasks should
		be put at the front of the queue.

        - EPoll
                Simple class that wraps epoll()/poll(). Note: must support
		oneshot polling and rearming.

        - JobQueue
                Maintain a queue of jobs, uses executor to execute them
                in order. Two jobs in the same JobQueue will never run
                at the same time.

        - Mainloop
                Uses executor, epoll.
                - Takes filedescriptors and callbacks. calls
                        back when descriptor is readable/writable/etc.
		- The callbacks are put on a job queue, which is passed in
		- Each callback is a oneshot - ie., after calling, the
		  filedescriptor is not polled again until it is rearmed.
		  The mainloop will have a method to rearm filedescriptors.
		  This ensures that the mainloop can start a new poll()
		  whenever it wants without waiting for all the callbacks
		  to finish.
                - Also takes timeouts that can be canceled. Timeouts also
		  need to be put on a queue.

		Sketch of implementation:

			polljob()
			{
				timeout = compute min_timeout ();
				if (timeout > 0)
					poll_armed_describtors(timeout);
				call all timeouts (ie., put them on queues);
				call all callbacks (ie., put them on queues);
				schedule (polljob);
			}

	- MainContext

		- The thing clients will deal with

		- Uses the main loop

		- Filedescriptors
			maincontext is responsible for rearming the
			descriptor after the client callback has been
			called.

		- Timeouts

		- Idle handlers

		- Everything associated with a main context happens 
		  serialized - ie., as if only one thread executed it.

		- This means a client structure doesn't need to be locked.

		- Has a get_executor() method so that clients can parallelize
		  if they want to.

		- Current thinking is that if we have main contexts, who
		  really needs a main loop? The only thing you could do
		  with it is to pass it to main contexts.

		  OTOH that's true of several of the objects here.
		  see notes at top of maincontext.c

		- Worth noting that stuff that has to be passed in to
		  create a client object must be available to the 
		  listener callback. But see http_server.c for an example

		- Err, the epoll in itself is not enough for maincontext,
		  since it needs to be shared between maincontexts. Ie., 
		  who would call epoll_wait(). We do really need a main
		  loop that will call back.

        - Listener
                Listens on a port. Calls back with a file descriptor when
		someone connects.

	- Connection
		initialized with a MainContext and a filedescriptor.
		Creates events when something happens

	- Http:
		Has a Connection
		parses http, emits events such as
			"get hostname pagename query"
			"post etc etc etc"
	
	- ContentProvider