selfboot / AnnotatedShadowSocks

Annotated shadowsocks(python version)
Other
3 stars 1 forks source link

EventLoop for processing multi I/O socket #20

Open selfboot opened 7 years ago

selfboot commented 7 years ago

Event Loop is a model of concurrent execution. The purpose of the model is to deal with asynchronous events efficiently. By asynchronous I mean events that happen at unpredictable points in time. In case of a web server in Python that would be client connections, socket reads, socket writes.

It works roughly like this:

  1. Block the main thread and wait for any events we're interested in
  2. Once an event occur, execute all callbacks for this event on the main thread one by one
  3. Once all callbacks are executed, check if anything interesting happened in the meantime. If so - go to step 2 and launch corresponding callbacks. If not – go to step 1 block and wait until the next event occurs.

There are different mechanisms for Operating systems to manage the I/O (input/output) events. These mechanisms let your code do something else while it's waiting. They provide notifications to your program for each of a range of events. You write what's called a "callback" function which is called by this part of the OS when those events happen, at which point your code knows that more input data is available to process.

Different OS have their own implementations of this mechanism, each of which handle it slightly differently, kqueue is what the BSD family of OS use, select is in some SysVs, /dev/poll is in Solaris and epoll is Linux.

Interfaces

To make the program run on different platforms, shadowsocks encapsulate the underlying implementation into EventLoop class, and provide some uniform interfaces for use.

poll

Polls the set of registered file descriptors, and returns a possibly-empty list containing (socket, fd, event) 3-tuples for the descriptors that have events or errors to report. fd is the file descriptor, and event is a bitmask with bits set for the reported events for that descriptor — POLLIN for waiting input, POLLOUT to indicate that the descriptor can be written to, and so forth.

An empty list indicates that the call timed out and no file descriptors had any events to report. If timeout is given, it specifies the length of time in milliseconds which the system will wait for events before returning. If timeout is omitted, negative, or None, the call will block until there is an event for this poll object.

add

Register a file descriptor with the polling object. Future calls to the poll() method will then check whether the file descriptor has any pending I/O events. f can be either an integer, or an object with a fileno() method that returns an integer. File objects implement fileno(), so they can also be used as the argument.

mode is an optional bitmask describing the type of events you want to check for, and can be a combination of the constants POLLIN, and POLLOUT. Registering a file descriptor that’s already registered is not an error, and has the same effect as registering the descriptor exactly once.

remove

Remove a file descriptor being tracked by a polling object. Just like the add() method, f can be an integer or an object with a fileno() method that returns an integer. Attempting to remove a file descriptor that was never registered causes a KeyErrorexception to be raised.

modify

Modifies an already registered f. This has the same effect as register(f, eventmask). Attempting to modify a file descriptor that was never registered causes an IOError exception with errno ENOENT to be raised.

Ref
Network Programming: What do the commands epoll, kqueue, /dev/poll and select do?
select / poll / epoll: practical difference for system architects
What are event loops in Python, what do they do?
Scalable Event Multiplexing: epoll vs. kqueue