]> mj.ucw.cz Git - libucw.git/blob - ucw/doc/mainloop.txt
Main: Preliminary implementation of record-based I/O
[libucw.git] / ucw / doc / mainloop.txt
1 Mainloop
2 ========
3
4 Not every program is strictly sequential. Sometimes, an event-driven
5 model is much easier to grasp. A fine example of such a program could
6 be a railway server. It has a separate connection to each station
7 and also to each train, so that it knows where each of them is (and
8 that neither a train nor a station have got missing). So it has to wait
9 for events coming from these connections and handle them appropriately.
10 It also processes other events that it has itself generated -- for
11 example various timers telling that a train is scheduled to depart
12 from some station.
13
14 The mainloop module takes care of the low-level part of event-driven
15 programs: it provides an event loop (often called a main loop), which
16 watches for events requested by the rest of the program and calls
17 a supplied callback when an event happens.
18
19 More precisely, for each type of an event (file descriptor activity,
20 timer etc.), there is a +handler structure+, which contains the description
21 of the event (e.g., the time where the timer should fire), a pointer to
22 a +handler function+ (the event callback) and data for use by the handler
23 function. The handler is then registered with the main loop.
24
25 - <<simple,Simple use>>
26 - <<contexts,Using multiple contexts>>
27 - <<threads,Forking and threading>>
28 - <<basic,Basic operations>>
29 - <<time,Time and timers>>
30 - <<file,Activity on file descriptors>>
31 - <<blockio,Asynchronous block I/O>>
32 - <<recordio,Asynchronous record I/O>>
33 - <<hooks,Loop hooks>>
34 - <<process,Child processes>>
35 - <<signal,Synchronous delivery of signals>>
36
37 [[contexts]]
38 Simple use
39 ----------
40
41 Simple programs usually employ the main loop in a straightforward way:
42
43 - Call @main_init() to initialize the main loop machinery.
44 - Add an initial set of event handers (@file_add(), @timer_add(), etc.).
45 - Enter the event loop by calling @main_loop(). This function runs for
46   the rest of the lifetime of the program. It watches for events and
47   handles them by calling the appropriate handler functions. These functions
48   can of course add new events or modify/delete the existing ones.
49 - When the program decides it wants to stop, it calls @main_shut_down(),
50   or alternatively it returns <<enum_main_hook_return,`HOOK_SHUTDOWN`>> from some hook functions.
51   Soon after that, @main_loop() returns.
52 - Remove all event hooks and call @main_cleanup().
53
54 The event structures (like <<struct_main_file,`struct main_file`>>) are
55 always allocated by the user, but please touch only the fields marked
56 in this documentation with `[*]`. The other fields are used internally;
57 you should initialize them to zeroes before adding the event and avoid
58 accessing them afterwards.
59
60 [[contexts]]
61 Using multiple contexts
62 -----------------------
63
64 In a more complex program, it can be useful to keep several sets of events
65 and run a separate instance of the event loop for each such set. A typical
66 example would be a multi-threaded program or a function which needs to
67 communicate with a network server locally, ignoring all other events
68 before the operation is finished.
69
70 For such cases, you can create multiple instances of <<struct_main_context,`struct main_context`>>
71 by calling @main_new(). Each thread then keeps its own current context,
72 which can be changed by @main_switch_context(). All mainloop functions
73 then either take an explicit pointer to a context or (more typically)
74 they operate on the current context. When you no longer need the context, you
75 can delete it by @main_delete().
76
77 It is even possible to use nested main loops: in a hook called by the
78 top-level instance of @main_loop(), you can switch to a different context,
79 call @main_loop() recursively and when you are done, switch back and return
80 to the top-level loop.
81
82 *CAVEAT:* In the present implementation, only a single context per process
83 can handle process exit events. If you use @process_add() in multiple contexts,
84 it can happen that the current context catches the `SIGCHLD` signal and obtains
85 information about a child process associated with another context, which it does
86 not know how to handle. If you ever need this, please let us know.
87
88 [[threads]]
89 Forking and threading
90 ---------------------
91
92 Using the event loop in a multi-threaded or multi-process program is possible,
93 but it should be done very carefully.
94
95 Multiple threads can use the main loop, but each of them must use a separate
96 context (or contexts).
97
98 When you fork() a child process, either the parent or the child must give up
99 use of each main loop context. The @main_teardown() and @main_destroy() functions
100 can be useful for that. (The reason is that some parts of the main loop context,
101 like file descriptors used internally, become shared between the processes, so
102 the processes could influence each other in crazy ways. You do not want to hunt
103 for such bugs.)
104
105
106
107 !!ucw/mainloop.h