aboutsummaryrefslogtreecommitdiffstats
path: root/fnet/src/vespa/fnet/iocomponent.h
blob: b88b2700db5f99bb7a7606e5e3b995dc26f38ecb (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
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include "scheduler.h"
#include <vespa/vespalib/net/selector.h>
#include <vespa/vespalib/util/ref_counted.h>
#include <mutex>
#include <condition_variable>
#include <chrono>

class FNET_IServerAdapter;
class FNET_TransportThread;
class FNET_Config;

/**
 * This is the common superclass of all components that may be part of
 * the transport layer event based I/O framework. Note that all IO
 * Components do IO against the network and that they use sockets to
 * perform that IO.
 **/
class FNET_IOComponent : public vespalib::enable_ref_counted
{
    friend class FNET_TransportThread;

    using Selector = vespalib::Selector<FNET_IOComponent>;

    struct Flags {
        Flags(bool shouldTimeout) :
            _ioc_readEnabled(false),
            _ioc_writeEnabled(false),
            _ioc_shouldTimeOut(shouldTimeout),
            _ioc_added(false),
            _ioc_delete(false)
        { }
        bool  _ioc_readEnabled;   // read event enabled ?
        bool  _ioc_writeEnabled;  // write event enabled ?
        bool  _ioc_shouldTimeOut; // component should timeout ?
        bool  _ioc_added;         // was added to event loop
        bool  _ioc_delete;        // going down...
    };
protected:
    FNET_IOComponent        *_ioc_next;          // next in list
    FNET_IOComponent        *_ioc_prev;          // prev in list
    FNET_TransportThread    *_ioc_owner;         // owner(TransportThread) ref.
    Selector                *_ioc_selector;      // attached event selector
    std::string              _ioc_spec;          // connect/listen spec
    Flags                    _flags;             // Compressed representation of boolean flags;
    int                      _ioc_socket_fd;     // source of events.
    vespalib::steady_time    _ioc_timestamp;     // last I/O activity
    std::mutex               _ioc_lock;          // synchronization
    std::condition_variable  _ioc_cond;          // synchronization

public:
    FNET_IOComponent(const FNET_IOComponent &) = delete;
    FNET_IOComponent &operator=(const FNET_IOComponent &) = delete;

    /**
     * Construct an IOComponent with the given owner. The socket that
     * will be used for IO is also given. The reason for this is to
     * enable the IOC superclass to handle all event registration and
     * deregistration without having to rely on code located in
     * subclasses.
     *
     * @param owner the TransportThread object owning this component
     * @param socket_fd the socket handle used by this IOC
     * @param spec listen/connect spec for this IOC
     * @param shouldTimeOut should this IOC time out if idle ?
     **/
    FNET_IOComponent(FNET_TransportThread *owner, int socket_fd,
                     const char *spec, bool shouldTimeOut);


    /**
     * Destruct component.
     **/
    virtual ~FNET_IOComponent();


    /**
     * @return connect/listen spec
     **/
    const char *GetSpec() const { return _ioc_spec.c_str(); }

    /*
     * Get a guard to gain exclusive access.
     */
    std::unique_lock<std::mutex> getGuard() { return std::unique_lock<std::mutex>(_ioc_lock); }

    /**
     * @return the owning TransportThread object.
     **/
    FNET_TransportThread *Owner() { return _ioc_owner; }


    /**
     * Get the configuration object associated with the owning transport
     * object.
     *
     * @return config object.
     **/
    const FNET_Config & getConfig() const;


    /**
     * @return whether this component should time-out if idle.
     **/
    bool ShouldTimeOut() { return _flags._ioc_shouldTimeOut; }


    /**
     * Update time-out information. This method simply performs a
     * proxy-call to the owning transport object, calling
     * FNET_TransportThread::UpdateTimeOut() with itself as parameter.
     **/
    void UpdateTimeOut();

    /**
     * Attach an event selector to this component. Before deleting an
     * IOC, one must first call detach_selector to detach the
     * selector.
     *
     * @param selector event selector to be attached.
     **/
    void attach_selector(Selector &selector);

    /**
     * Detach from the attached event selector. This will disable
     * future selector events.
     **/
    void detach_selector();

    /**
     * Enable or disable read events.
     *
     * @param enabled enabled(true)/disabled(false).
     **/
    void EnableReadEvent(bool enabled);


    /**
     * Enable or disable write events.
     *
     * @param enabled enabled(true)/disabled(false).
     **/
    void EnableWriteEvent(bool enabled);


    //----------- virtual methods below ----------------------//

    /**
     * Used to identify which components are related to a specific
     * server adapter to be able to perform partial shutdown.
     *
     * @return the server adapter attached to this component
     **/
    virtual FNET_IServerAdapter *server_adapter() = 0;

    /**
     * This function is called as the first step of adding an io
     * component to the selection loop. The default implementation
     * will always return true. This can be overridden to perform
     * delayed setup in the network thread. If this function returns
     * false, the component is broken and should be closed
     * immediately.
     *
     * @return false if broken, true otherwise.
     **/
    virtual bool handle_add_event();

    /**
     * This function is called by the transport thread to handle the
     * completion of an asynchronous invocation of
     * 'do_handshake_work'. This functionality is used by TLS
     * connections in order to move expensive cpu work out of the
     * transport thread. If this function returns false, the component
     * is broken and should be closed immediately.
     *
     * @return false if broken, true otherwise.
     **/
    virtual bool handle_handshake_act();


    /**
     * Close this component immediately. NOTE: this method should only
     * be called by the transport thread. If you want to close an IO
     * Component from another thread you should use the
     * FNET_TransportThread::Close method instead (with the IOC you want to
     * close as parameter).
     **/
    virtual void Close() = 0;


    /**
     * Called by the transport thread when a read event has
     * occurred.
     *
     * @return false if broken, true otherwise.
     **/
    virtual bool HandleReadEvent() = 0;


    /**
     * Called by the transport thread when a write event has
     * occurred.
     *
     * @return false if broken, true otherwise.
     **/
    virtual bool HandleWriteEvent() = 0;
};