aboutsummaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/net/selector.h
blob: 6b40053abe1781ce9b55a95e33078e277277c831 (plain) (blame)
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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include "wakeup_pipe.h"
#ifdef __APPLE__
#include "emulated_epoll.h"
#else
#include "native_epoll.h"
#endif
#include <vector>

namespace vespalib {

/**
 * Simple class used to hold events extracted from a call to epoll_wait.
 **/
class EpollEvents
{
private:
    std::vector<epoll_event> _epoll_events;
    size_t                   _num_events;
public:
    EpollEvents(size_t max_events) : _epoll_events(max_events), _num_events(0) {}
    void extract(Epoll &epoll, int timeout_ms) {
        _num_events = epoll.wait(&_epoll_events[0], _epoll_events.size(), timeout_ms);
    }
    const epoll_event *begin() const { return &_epoll_events[0]; }
    const epoll_event *end() const { return &_epoll_events[_num_events]; }
    size_t size() const { return _num_events; }
};

//-----------------------------------------------------------------------------
enum class SelectorDispatchResult {WAKEUP_CALLED, NO_WAKEUP};

template <typename Context>
class Selector
{
private:
    Epoll       _epoll;
    WakeupPipe  _wakeup_pipe;
    EpollEvents _events;
public:
    Selector()
        : _epoll(), _wakeup_pipe(), _events(4096)
    {
        _epoll.add(_wakeup_pipe.get_read_fd(), nullptr, true, false);
    }
    ~Selector() {
        _epoll.remove(_wakeup_pipe.get_read_fd());
    }
    void add(int fd, Context &ctx, bool read, bool write) { _epoll.add(fd, &ctx, read, write); }
    void update(int fd, Context &ctx, bool read, bool write) { _epoll.update(fd, &ctx, read, write); }
    void remove(int fd) { _epoll.remove(fd); }
    void wakeup() { _wakeup_pipe.write_token(); }
    void poll(int timeout_ms) { _events.extract(_epoll, timeout_ms); }
    size_t num_events() const { return _events.size(); }
    template <typename Handler>
    SelectorDispatchResult dispatch(Handler &handler) {
        SelectorDispatchResult result = SelectorDispatchResult::NO_WAKEUP;
        for (const auto &evt: _events) {
            if (evt.data.ptr == nullptr) {
                _wakeup_pipe.read_tokens();
                handler.handle_wakeup();
                result = SelectorDispatchResult::WAKEUP_CALLED;
            } else {
                Context &ctx = *((Context *)(evt.data.ptr));
                bool read = ((evt.events & (EPOLLIN  | EPOLLERR | EPOLLHUP)) != 0);
                bool write = ((evt.events & (EPOLLOUT  | EPOLLERR | EPOLLHUP)) != 0);
                handler.handle_event(ctx, read, write);
            }
        }
        return result;
    }
};

//-----------------------------------------------------------------------------

/**
 * Selector used to wait for events on a single file
 * descriptor. Useful for testing or sync wrappers. Note: do not use
 * for performance-critical code.
 **/
class SingleFdSelector
{
private:
    int _fd;
    Selector<int> _selector;

public:
    SingleFdSelector(int fd);
    ~SingleFdSelector();

    // returns true when readable or false on wakeup
    bool wait_readable();

    // returns true when writable or false on wakeup
    bool wait_writable();

    // make wait_readable/wait_writable return false immediately
    void wakeup();
};

//-----------------------------------------------------------------------------

} // namespace vespalib