⚙️

Asynchronous I/O Library (Asio)

Jul 1, 2024

Lecture Notes: Asynchronous I/O Library (Asio)

Introduction

  • Library: Asynchronous I/O Library (Asio)
  • Purpose: Efficiently handle numerous simultaneous I/O operations.
  • Model Used: Proactor model
    • Goal: Handle multiple connections and various I/O activities (e.g., network, serial ports, job descriptors, timers).
  • Recommendation: Start with the overview section of the reference material.

Concept of Asynchronous I/O

  • Analogy: Making coffee — Asynchronous vs. Synchronous transactions
    • Asynchronous: Request for coffee while continuing to work until the coffee is delivered (multiple tasks handled concurrently).
    • Synchronous: Request for coffee requires continuous supervision, leading to waiting (tasks handled one after another).
  • Example Function: read_file(file_name, buffer, handler) — initiates file reading operation and calls the handler upon completion.
  • Benefits: Reduced resource usage and better scalability.
  • Issues: Complexity in handling concurrent operations in different threads.

Proactor Model Explained

  • Storytime Representation: Family and a beach scenario, illustrating the proactor model components:
    • Initiator (Dad): Application making asynchronous requests.
    • Proactor/Butler: Manages completion events, handling the results.
    • Asynchronous Operation Processor (Owner): Performs the requested I/O operations.
    • Completion Handler (Johnny): The application code that handles the completion of the I/O operation.
  • Key Lessons:
    • Family is unaware of internal activities in the shack (abstraction provided by the model).
    • The application must supply a thread (Butler) for delivering the results.
    • Resources (cups) must remain available until the transaction completes.

Implementing Asynchronous I/O

  • Timer Example: Using a timer to illustrate the basics.
    • Code Snippet: // pseudo-code void timer_expired(const boost::system::error_code& err) { std::cout << "Timer expired"; } boost::asio::io_service io_service; boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(5)); timer.async_wait(&timer_expired); io_service.run();
    • Conclusion: Asynchronous I/O begins with the request (async_wait). The actual operation starts immediately.

Concepts: Bind and Strands

  • Bind: Creating function objects (functors) for delayed execution with specific arguments.
    • Example: // pseudo-code auto functor = boost::bind(divide, _1, _2); int result = functor(10, 5); // result = 2
  • Strand: Ensures that handlers do not execute concurrently.
    • Code Snippet: // pseudo-code boost::asio::io_service io_service; boost::asio::strand strand(io_service); strand.post(boost::bind(&some_function, some_object)); strand.post(boost::bind(&another_function, another_object)); io_service.run();

Shared Pointers and Lifetime Management

  • Standard Shared Pointer:
    • Used for reference counting and ensuring the object's lifetime.
    • Example: // pseudo-code std::shared_ptr<MyClass> ptr(new MyClass); boost::asio::io_service io_service; io_service.post(boost::bind(&MyClass::my_method, ptr, arg1));
  • Enable Shared from This:
    • Ensures proper lifetime management when dealing with shared pointers.
    • Code Snippet: // pseudo-code class MyClass : public std::enable_shared_from_this<MyClass> { void my_method(int arg1) { auto self = shared_from_this(); // do something } };

Techniques and Practical Examples

  • Async Read and Write: Using buffer wrappers for memory management.
    • Free Function: boost::asio::buffer(data, size).
  • Post Operations:
    • Adding tasks to the completion queue immediately.
    • Example: // pseudo-code io_service.post(boost::bind(&some_function, arg1, arg2));
  • Proactor Model Application: XML server handling client requests asynchronously.
    • Key Steps:
      1. Accept connections and pass to handlers.
      2. Handlers manage individual client communication using Asio functions (async_read, async_write).
      3. Use strands to ensure handlers do not interleave incorrectly.

Advanced Topics

  • Handling Multiple Reeds and Writes:
    • Use queueing mechanism and strands to manage concurrent data.
    • Avoid multiple threads writing to the same socket simultaneously.
  • Optimizing Asio Usage:
    • Use additional services or signals to distribute workload.
  • Conclusion: Utilize Asio's functionality and models to handle complex I/O concurrently without resource wastage.

Questions and Common Inquiries

  • Issues with throwing exceptions inside handlers (handlers should catch their exceptions).
  • Utilizing strands and signals to avoid blocking the I/O service's run loop.
  • Error handling and cleanup for asynchronous operations.

Resources and Further Information

  • Final Notes: Use provided resources and examples to experiment with Asio library functionalities.
  • Slides and Code: Available for download and further study.