bdcae91cfc3f999f1daefc28722510a93ad5557d
[citadel.git] / citadel / docs / citadel_thread_io.txt
1 ====== How Threads & IO for clients work ======
2 Citserver is a multi thread process. It has four types of threads: 
3   - Workers - these are anonymous threads which can do any of the server related functions like Imapserver; smtpserver; citadel protocol and so on.
4   - Housekeeping - once per minute one Worker becomes the Housekeeping thread which does recurrent jobs like database maintenance and so on.
5   - IO - one thread is spawned to become the IO-Thread at startup. Inside it lives the event-queue which is implemented by libev.
6   - DB-IO - one thread is spawned to become the database IO thread. Inside it lives another event-queue, which is also implemented by libev.
7
8
9 ===== Worker Threads =====
10 The workers live blocking on selects to server sockets. Once IO on a server socket becomes available, one thread awakes, and takes the task.
11 It is then assigned a CitContext structure on a Thread global var (accessible via the CC macro), which then becomes his personality and controls what the thread is going to do with the IO,
12 and which server code (Imap/SMTP/Citadel/...) is going to evaluate the IO and reply to it.
13
14 ===== Housekeeping Thread =====
15 Once every minute one Worker becomes something special: the Housekeeping Thread. Only one Houskekeeper can exist at once. As long as one Housekeeper is running no other is started.
16 Jobs done by the Housekeeper are registered via the <>-API. The current list:
17
18   - DB maintenance; flush redo logs etc.
19   - fulltext index (this one is a little problematic here, since it may be long running and block other housekeeping tasks for a while)
20   - Networking-Queue
21   - Citadel Networking Queue aggregator (Blocks NTT)
22   - Citadel Inbound Queue operator (Blocks NTT)
23   - Citadel Networking Queue
24   - SMTP-Client-Queue
25   - RSS-Aggregator scheduling
26   - HTTP-Message Notification Queue (Extnotify)
27   - POP3Client Queue
28
29 The queues operate over their respective queue; which may be implemented with a private room (smtp-Queue, Extnotify Client), per room configs (RSS-Client; POP3Client), or an aggregate of Rooms/Queue/Roomconfigs (Citadel Networker).
30
31
32 ==== Networking Queue ====
33 The networking queue opperats over an aggregate of per room network configs and messages in rooms that are newer than a remembered whatermark (netconfigs.txt#lastsent). 
34 All messages Newer than the watermark are then processed: 
35   - for all mailinglist recipients an entry is added to the mailqueue
36   - for all mailinglist digest recipients a digest file is written, or once per day scheduled to 
37   - for all Citadel networking nodes a spool file is written for later evaluation.
38
39 ==== Citadel Networking Queue aggregator ====
40 The aggregator iterates over the spool directory, and combines all per message spool files to one big file which is to be sent by the Citadel Networking Queue later.
41 It blocks the NTT-List for the remote party while doing so; if the NTT-List is blocked the file is skipped for next operation. Once all files are successfully processed, all files not associated with a networking config are flushed.
42
43 ==== Citadel Inbound Queue operator ====
44 The inbound queue operator scans for new inbound spool files, processes them, splits them into messages which are then posted into their respective rooms.
45
46
47 ==== other Queues ====
48 Once these Queue-operators have identified a job to be performed like 
49   - talking to another Citadel Node to transfer spool files back and forth
50   - sending mails via SMTP
51   - polling remote POP3-Servers for new mails
52   - fetching RSS-Feeds via HTTP
53   - Notifying external facilities for new messages in inboxes via HTTP
54
55 They create a Context for that Job (AsyncIO struct) plus their own Private data, plus a CitContext which is run under the privileges of a SYS_* user.
56 HTTP Jobs just schedule the HTTP-Job, and are then called back on their previously registered hook once the HTTP-Request is done.
57
58 All other have to implement their own IO controled business logic.
59
60
61 ===== IO-Thread =====
62 The IO-Event-queue lives in this thread. Code running in this thread has to obey several rules.
63   - it mustn't do blocking operations (like disk IO or nameserver lookups other than the c-ares facilities) 
64   - it mustn't use blocking Sockets
65   - other threads mustn't access memory belonging to an IO job
66   - New IO contexts have to be registered via QueueEventContext() from other threads; Its callback are then called from whithin the event queue to start its logic.
67   - New HTTP-IO contexts have to be registered via QueueCurlContext() 
68   - once you've registered a context, you must not access its memory anymore to avoid race conditions.
69
70 ==== Logic in event_client ====
71 InitIOStruct() which prepares an AsyncIO struct for later use before using QueueEventContext() to activate it.
72 Here we offer the basics for I/O; Hostname lookus, connecting remote parties, basic read and write operation control flows.
73 This logic communicates with the next layer via callback hooks. Before running hooks or logging CC is set from AsyncIO->CitContext
74 Most clients are stacked that way: 
75
76 <event base> --- <IO grammer logic> -- <state machine dispatcher> -- <state hooks>
77
78 === event base ===
79 While the event base is responsible for reading and writing, it just does so without the understanding of the protocol it speaks. It at most knows about chunksises to send/read, or when a line is sent / received.
80 === IO grammer logic ===
81 The IO grammer logic is implemented for each client. It understands the grammer of the protocol spoken; i.e. the SMTP implementation knows that it has to read status lines as long as there are 3 digits followed by a dash (i.e. in the ehlo reply). Once the grammer logic has decided a communication state is completed, it calls the next layer.
82
83 === state machine dispatcher ===
84 The state machine dispatcher knows which part of the application logic is currently operated. Once its called, it dispatches to the right read/write hook. Read/write hooks may in term call the dispatcher again, if they want to divert to another part of the logic.
85
86 === state hooks ===
87 The state hooks or handlers actualy PARSE the IO-Buffer and evaluate what next action should be taken; 
88 There could be an error from the remote side so the action has to be aborted, or usually simply the next state can be reached.
89
90
91 === surrounding businesslogic ===
92 Depending on the protocol there has to be surrounding business logic, which handles i.e. nameserver lookups, which ips to connect, what to do on a connection timeout, or doing local database IO.
93
94 ===== DB-IO-Thread =====
95 Since lookups / writes to the database may take time, this mustn't be done inside of the IO-Eventloop. Therefore this other thread & event queue is around. 
96 Usually a context should respect that there may be others around also wanting to do DB IO, so it shouldn't do endless numbers of requests.
97 While its ok to do _some_ requests in a row (like deleting some messages, updating messages, etc.),
98 loops over unknown numbers of queries should be unrolled and be done one query at a time (i.e. looking up messages in the 'already known table')
99
100 === Transitioning back & forth ===
101 If the application logic has found that a transition is to be made from the event IO to the DB IO thread. Do so by calling DBQueueEventContext() (DB -> IO) EventQueueDBOperation() (IO->DB) and pass its return value up the row to enable the controll logic to do the actual transition for you.