2

The reactor pattern which is utilized by libuv for handling IO is synchronous by design but libuv supports async io. How is this possible? Does libuv extend the reactor's design somehow to support async io? Does using multiple threads/event loops aid in achieving this?

Michael
  • 73
  • 1
  • 8

1 Answers1

2

The I/O model of Node and libuv is very similar to what nginx does internally.

The libuv uses a single-threaded event loop and non-blocking asynchronous I/O. All functions are synchronous in a way that they run to completion but some clever hackery with promises and generators can be used to appear that they don't (when in fact both the invocation of the generator function is non-blocking and returns the generator object immediately and the generator methods like .next() run to completion), plus the new async/await syntax makes it very convenient.

For operations that cannot be accomplished in a non-blocking way Node uses a thread pool to run the blocking operations in separate threads but this is done transparently and it is never exposed to the application code written in JavaScript (you need to step down to C++ to work with that directly).

See: http://docs.libuv.org/en/v1.x/design.html

Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on, so the current approach is to run blocking file I/O operations in a thread pool. [...]

libuv currently uses a global thread pool on which all loops can queue work on. 3 types of operations are currently run on this pool:

  • File system operations
  • DNS functions (getaddrinfo and getnameinfo)
  • User specified code via uv_queue_work()

See also those answers for more details:

See the links and illustration in those answers. There are a lot of resources to read bout that topic.

rsp
  • 107,747
  • 29
  • 201
  • 177
  • Those are good references but none of them discuss the matter specifically. Let me ask a more specific question. Since all network io runs on a single thread within libuv, to achieve async io with something read events for example, you would have to delegate the task for the OS and provide it with a user defined buffer that would be filled with the data from the read op, but that would be the proactor pattern. How does libuv achieve a similar effect by using a single threaded reactor event loop? – Michael Jul 20 '17 at 13:24
  • @Michael Most network I/O runs on a single therad but e.g. not the DNS operations (getaddrinfo and getnameinfo) and not the file system I/O so this is not as clear-cut as it may seem and no single theoretical pattern can be applied in 100% to all of the edge cases that Node needs to support on all platforms. – rsp Jul 25 '17 at 08:28
  • @Michael The first answer from the list that I included in the answer has some comments that you might find useful posted by [**saghul**](https://stackoverflow.com/users/280310/saghul?tab=topactivity) (who I guess would give you a much more detailed answer to your comment here but unfortunately it is only possible to @-mention someone in comments to answer where he already commented). – rsp Jul 25 '17 at 08:28