aboutsummaryrefslogtreecommitdiffstats
path: root/doc/channel_multiplexing
diff options
context:
space:
mode:
Diffstat (limited to 'doc/channel_multiplexing')
-rw-r--r--doc/channel_multiplexing84
1 files changed, 84 insertions, 0 deletions
diff --git a/doc/channel_multiplexing b/doc/channel_multiplexing
new file mode 100644
index 0000000..a1ba681
--- /dev/null
+++ b/doc/channel_multiplexing
@@ -0,0 +1,84 @@
+This document is mostly for the developer to settle down ideas that are sitting
+in his mind.
+
+Server structure:
+Main thread: accepting TCP socket and reading-writing TCP sockets created by
+an accepting one. TODO: rewrite so that the entire system is using poll.
+
+Other `get_nprocs() - 1` threads (if `get_nprocs() == 1`, then only 1
+additional thread) are channel pools.
+
+Additionally, main thread maintains a list of all channels and threads
+associated with them. When new channel is requested, is is created on a thread
+that is maintaining the least amount of channels at the time.
+
+Each channel pool consists of an anonymous UNIX domain socket (which is used
+for recieving system messages from main thread) and some amount of INET domain
+sockets. All of them are switched on using `poll` system call.
+
+Each channel in the channel pool has an associated list of peers with it. When
+data is recieved on a channel, it is forwarded to every peer (except the
+sender).
+
+The basic structure of the loop should look something like this:
+
+```
+fds = {timerfd, master socket, udp1, udp2, ...};
+while (poll(fds, nfds, -1) > 0) {
+ if (socket is master and has data) process_system_commands();
+ if (timerfd is ready) check_keepalives(all sockets);
+ for (every socket that has data)
+ handle_peers(socket);
+}
+
+void process_system_commands() {
+ command = read(master);
+ switch (parse_command()) {
+ case add_user: addto(channel, user); break;
+ case add_channel: array_append(channels, new_channel()); break;
+ case remove_user: rmfrom(channel, user); break;
+ case remove_channel: array_pop(channel); break;
+ }
+}
+
+void handle_peers(int socket) {
+ data = read(socket);
+ for (every peer of socket)
+ sendto(socket, data, peer, O_NONBLOCK); // MSG_DONTWAIT, maybe?
+}
+
+void check_keepalives(int socket) {
+ for (every peer of socket) {
+ if (keepalive of peer too old) rmfrom(channel, user);
+ }
+}
+```
+
+Data structures:
+inside main thread:
+ struct channel{u64 id, int port};
+ struct channel_pool {
+ u16 num,
+ int master_pipe,
+ u64 thread_id,
+ array<struct channel> chs
+ };
+ // modern linux kernel has limit on maximim amount of cores:
+ // 8192. this would break 65535 port limit very fast anyways.
+ array<struct channel_pool> threads;
+inside pool:
+ struct ch_user { u64 id, u32 ip, u16 port, u64 last_keepalive };
+ struct channel {
+ u64 id,
+ u64 owner,
+ int udo,
+ hash_set<ch_user>* users
+ };
+ array<struct channel> channels;
+ int master = pipe
+
+Communication between threads will be done through pipes. Yes, this will be
+inefficient in a sense that there will be 2 file descriptors opened for each
+thread, but what are other options? Yes, none. If there are, mail this to me
+please
+