【boost::asio allocation】async関数のハンドラ

# 今回のお題
boost1_57_0 allocation(C++11)
http://www.boost.org/doc/libs/1_57_0/doc/html/boost_asio/examples/cpp11_examples.html

上記サンプルを CMakeプロジェクトにしたものを
下記リポジトリに置いておきます
https://github.com/MurasameOpen/CPPSample/tree/master/boostSample/boost1_57_0/asio/cpp11/allocation


# 解析

##動作
待ち受けポートをパラメータに 起動し
recvしたデータをそのまま返す echo動作をします

telnetで接続してみましょう

```bash:telnet
miyatake_y$ telnet -4 localhost 8080 
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
12345
12345
```



## ソース解析

基本は buffersと同じです
ハンドラーの部分に独自メモリアロケータを使用し
効率化をはかってます

### ソース

class server
は、buffers参照。
shared_ptrを使いセッションを管理しています


```cpp:session

inline custom_alloc_handler<Handler> make_custom_alloc_handler(
    handler_allocator& a, Handler h)
{
  return custom_alloc_handler<Handler>(a, h);
}

class session
  : public std::enable_shared_from_this<session>
{
public:
  session(tcp::socket socket)
    : socket_(std::move(socket))
  {
  }

  void start()
  {
    do_read();
  }

private:
  void do_read()
  {
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(data_),
        make_custom_alloc_handler(allocator_,
          [this, self](boost::system::error_code ec, std::size_t length)
          {
            if (!ec)
            {
              do_write(length);
            }
          }));
  }

  void do_write(std::size_t length)
  {
    auto self(shared_from_this());
    boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
        make_custom_alloc_handler(allocator_,
          [this, self](boost::system::error_code ec, std::size_t /*length*/)
          {
            if (!ec)
            {
              do_read();
            }
          }));
  }

  // The socket used to communicate with the client.
  tcp::socket socket_;

  // Buffer used to store data received from the client.
  std::array<char, 1024> data_;

  // The allocator to use for handler-based custom memory allocation.
  handler_allocator allocator_;
};

```

std::enable_shared_from_this<session>
を使い、shared_ptrをラムダのキャプチャに登録し
sessionがdeleteされるのを防ぐ所はbuffersと同じです


make_custom_alloc_handler(allocator_, ラムダ・・
が今回の重要ポイント。
buffersでは単にラムダ式を入れてました
実際ここも、ラムダ式を直接登録しても動作しますが
毎回ハンドラの登録で newが発生します
custom_alloc_handlerでは そのnewが毎回発生しない仕組みを作っています



```cpp:custom_alloc_handler
class custom_alloc_handler
{
public:
  custom_alloc_handler(handler_allocator& a, Handler h)
    : allocator_(a),
      handler_(h)
  {
  }

  template <typename ...Args>
  void operator()(Args&&... args)
  {
    handler_(std::forward<Args>(args)...);
  }

  friend void* asio_handler_allocate(std::size_t size,
      custom_alloc_handler<Handler>* this_handler)
  {
    return this_handler->allocator_.allocate(size);
  }

  friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
      custom_alloc_handler<Handler>* this_handler)
  {
    this_handler->allocator_.deallocate(pointer);
  }

private:
  handler_allocator& allocator_;
  Handler handler_;
};

```

handler_allocator のラップをしています。

operator()(Args&&... args)  
は、ハンドラが呼ばれた時に呼び出されるメンバ関数
Asioからコールバックが来た際に、sessionで登録した ラムダ式を呼び出します
Args&&...  を使えば、色々な引数形式にも対応できますね
コレは便利

asio_handler_allocate、asio_handler_deallocate  のフレンド関数
ハンドラーは（恐らく）一度 Asioによってメモリコピーされますが
その時のメモリの allocate deallocateをオーバーロードします
ここでは handler_allocatorの allocate deallocateを呼びます



```cpp:handle_allocator
class handler_allocator
{
public:
  handler_allocator()
    : in_use_(false)
  {
  }

  handler_allocator(const handler_allocator&) = delete;
  handler_allocator& operator=(const handler_allocator&) = delete;

  void* allocate(std::size_t size)
  {
    if (!in_use_ && size < sizeof(storage_))
    {
      in_use_ = true;
      return &storage_;
    }
    else
    {
      return ::operator new(size);
    }
  }

  void deallocate(void* pointer)
  {
    if (pointer == &storage_)
    {
      in_use_ = false;
    }
    else
    {
      ::operator delete(pointer);
    }
  }

private:
  // Storage space used for handler-based custom memory allocation.
  typename std::aligned_storage<1024>::type storage_;

  // Whether the handler-based custom allocation storage has been used.
  bool in_use_;
};
```

ハンドラーのメモリ確保＆解放時に  allocate deallocateが呼ばれます
細かい部分は読んでもらうとして
  typename std::aligned_storage<1024>::type storage_;
で 1024Bytesのメモリを確保しています
メモリアライメントのために aligned_storage で確保
今回のサンプルでは この   handler_allocator はsessionで作成し参照しているので
session内で何度 async関数を使っても、再アロケートされません
そして、ハンドラーとして確保すべき領域が1024Bytes以下の場合は
storage_ に上書きをするので new deleteが行われずパフォーマンス向上
1024Bytesを超える場合は new deleteを使う

細かいようですが、read writeはかなりの頻度で使うので
ハンドラーの allocate deallocateを大幅に減らす事は
パフォーマンスに影響を及ぼすでしょう 




# 結論
ハンドラーのメモリ確保＆開放のコストは少なくはないと思うので
なるべくなら　カスタムアロケータを作り
コストを下げるべきだ