summaryrefslogtreecommitdiffstats
blob: cac8405928a47d83ff1729a66a101f00cb40e6ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//
// eventfd_select_interrupter.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
// Copyright (c) 2008 Roelof Naude (roelof.naude at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP
#define BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP

#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)

#include <boost/asio/detail/push_options.hpp>

#include <boost/asio/detail/push_options.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/detail/pop_options.hpp>

#if defined(linux)
# if !defined(BOOST_ASIO_DISABLE_EVENTFD)
#  include <linux/version.h>
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
#   define BOOST_ASIO_HAS_EVENTFD
#  endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
# endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
#endif // defined(linux)

#if defined(BOOST_ASIO_HAS_EVENTFD)

#include <boost/asio/detail/push_options.hpp>
#include <fcntl.h>
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
# include <asm/unistd.h>
#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
# include <sys/eventfd.h>
#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
#include <boost/asio/detail/pop_options.hpp>

#include <boost/asio/error.hpp>
#include <boost/asio/detail/socket_types.hpp>

namespace boost {
namespace asio {
namespace detail {

class eventfd_select_interrupter
{
public:
  // Constructor.
  eventfd_select_interrupter()
  {
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
    write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0);
#else // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
    write_descriptor_ = read_descriptor_ = ::eventfd(0, 0);
#endif // __GLIBC__ == 2 && __GLIBC_MINOR__ < 8
    if (read_descriptor_ != -1)
    {
      ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
    }
    else
    {
      int pipe_fds[2];
      if (pipe(pipe_fds) == 0)
      {
        read_descriptor_ = pipe_fds[0];
        ::fcntl(read_descriptor_, F_SETFL, O_NONBLOCK);
        write_descriptor_ = pipe_fds[1];
        ::fcntl(write_descriptor_, F_SETFL, O_NONBLOCK);
      }
      else
      {
        boost::system::error_code ec(errno,
            boost::asio::error::get_system_category());
        boost::system::system_error e(ec, "eventfd_select_interrupter");
        boost::throw_exception(e);
      }
    }
  }

  // Destructor.
  ~eventfd_select_interrupter()
  {
    if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_)
      ::close(write_descriptor_);
    if (read_descriptor_ != -1)
      ::close(read_descriptor_);
  }

  // Interrupt the select call.
  void interrupt()
  {
    uint64_t counter(1UL);
    int result = ::write(write_descriptor_, &counter, sizeof(uint64_t));
    (void)result;
  }

  // Reset the select interrupt. Returns true if the call was interrupted.
  bool reset()
  {
    if (write_descriptor_ == read_descriptor_)
    {
      // Only perform one read. The kernel maintains an atomic counter.
      uint64_t counter(0);
      int bytes_read = ::read(read_descriptor_, &counter, sizeof(uint64_t));
      bool was_interrupted = (bytes_read > 0);
      return was_interrupted;
    }
    else
    {
      // Clear all data from the pipe.
      char data[1024];
      int bytes_read = ::read(read_descriptor_, data, sizeof(data));
      bool was_interrupted = (bytes_read > 0);
      while (bytes_read == sizeof(data))
        bytes_read = ::read(read_descriptor_, data, sizeof(data));
      return was_interrupted;
    }
  }

  // Get the read descriptor to be passed to select.
  int read_descriptor() const
  {
    return read_descriptor_;
  }

private:
  // The read end of a connection used to interrupt the select call. This file
  // descriptor is passed to select such that when it is time to stop, a single
  // 64bit value will be written on the other end of the connection and this
  // descriptor will become readable.
  int read_descriptor_;

  // The write end of a connection used to interrupt the select call. A single
  // 64bit non-zero value may be written to this to wake up the select which is
  // waiting for the other end to become readable. This descriptor will only
  // differ from the read descriptor when a pipe is used.
  int write_descriptor_;
};

} // namespace detail
} // namespace asio
} // namespace boost

#endif // defined(BOOST_ASIO_HAS_EVENTFD)

#include <boost/asio/detail/pop_options.hpp>

#endif // BOOST_ASIO_DETAIL_EVENTFD_SELECT_INTERRUPTER_HPP