E-MailRelay

gfutureevent_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gfutureevent_unix.cpp
19///
20
21#include "gdef.h"
22#include "gfutureevent.h"
23#include "gprocess.h"
24#include "gmsg.h"
25#include "geventloop.h"
26#include <array>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <fcntl.h>
30
31//| \class GNet::FutureEventImp
32/// A pimple-pattern implementation class used by GNet::FutureEvent.
33///
34 class GNet::FutureEventImp : public EventHandler
35{
36 public:
37 FutureEventImp( FutureEventHandler & , EventState ) ;
38 // Constructor.
39
40 ~FutureEventImp() override ;
41 // Destructor.
42
43 static bool send( HANDLE , bool ) noexcept ;
44 // Writes to the write socket.
45
46 void receive() ;
47 // Reads from the socket to clear the event.
48
49 HANDLE handle() noexcept ;
50 // Extracts the socket fd as a handle.
51
52public:
53 FutureEventImp( const FutureEventImp & ) = delete ;
54 FutureEventImp( FutureEventImp && ) = delete ;
55 FutureEventImp & operator=( const FutureEventImp & ) = delete ;
56 FutureEventImp & operator=( FutureEventImp && ) = delete ;
57
58private: // overrides
59 void readEvent() override ; // Override from GNet::EventHandler.
60
61private:
62 static int init( int ) ;
63
64private:
65 struct Fd
66 {
67 Fd() = default;
68 ~Fd() { if(fd!=-1) ::close(fd) ; }
69 Fd & operator=( int fd_ ) { fd = fd_ ; return *this ; }
70 int fd{-1} ;
71 Fd( const Fd & ) = delete ;
72 Fd( Fd && ) = delete ;
73 Fd & operator=( const Fd & ) = delete ;
74 Fd & operator=( Fd && ) = delete ;
75 } ;
76
77 private:
78 FutureEventHandler & m_handler ;
79 Fd m_read ;
80 Fd m_write ;
81 bool m_triggered {false} ;
82} ;
83
84GNet::FutureEventImp::FutureEventImp( FutureEventHandler & handler , EventState es ) :
85 m_handler(handler)
86{
87 std::array<int,2U> fds {{ -1 , -1 }} ;
88 int rc = ::socketpair( AF_UNIX , SOCK_DGRAM , 0 , fds.data() ) ;
89 if( rc != 0 )
90 {
91 int e = G::Process::errno_() ;
92 throw FutureEvent::Error( "socketpair" , G::Process::strerror(e) ) ;
93 }
94 m_read = init( fds[0] ) ;
95 m_write = init( fds[1] ) ;
96 EventLoop::instance().addRead( Descriptor(m_read.fd) , *this , es ) ;
97}
98
99 int GNet::FutureEventImp::init( int fd )
100{
101 GDEF_IGNORE_RETURN ::fcntl( fd , F_SETFL , ::fcntl(fd,F_GETFL) | O_NONBLOCK ) ; // NOLINT
102 return fd ;
103}
104
105GNet::FutureEventImp::~FutureEventImp()
106{
107 if( m_read.fd >= 0 )
108 {
109 if( EventLoop::exists() )
110 EventLoop::instance().dropRead( Descriptor(m_read.fd) ) ;
111 }
112}
113
114HANDLE GNet::FutureEventImp::handle() noexcept
115{
116 int fd = -1 ;
117 std::swap( m_write.fd , fd ) ;
118 return static_cast<HANDLE>(fd) ;
119}
120
121 void GNet::FutureEventImp::receive()
122{
123 char c = '\0' ;
124 GDEF_IGNORE_RETURN ::recv( m_read.fd , &c , 1 , 0 ) ;
125}
126
127 bool GNet::FutureEventImp::send( HANDLE handle , bool close ) noexcept
128{
129 int fd = static_cast<int>(handle) ;
130 char c = '\0' ;
131 ssize_t rc = G::Msg::send( fd , &c , 1 , 0 ) ;
132 if( close )
133 ::close( fd ) ; // just after send() is okay
134 const bool ok = rc == 1 ;
135 return ok ;
136}
137
138 void GNet::FutureEventImp::readEvent()
139{
140 receive() ;
141 if( !m_triggered )
142 {
143 m_triggered = true ;
144 m_handler.onFutureEvent() ;
145 }
146}
147
148// ==
149
150 GNet::FutureEvent::FutureEvent( FutureEventHandler & handler , EventState es ) :
151 m_imp(std::make_unique<FutureEventImp>(handler,es))
152{
153}
154
155 GNet::FutureEvent::~FutureEvent()
156= default ;
157
158 bool GNet::FutureEvent::send( HANDLE handle , bool close ) noexcept
159{
160 return FutureEventImp::send( handle , close ) ;
161}
162
163HANDLE GNet::FutureEvent::handle() noexcept
164{
165 return m_imp->handle() ;
166}
167
GNet::EventHandler::readEvent
virtual void readEvent()
Called for a read event.
Definition: geventhandler.cpp:39
GNet::EventState
A lightweight object containing an ExceptionHandler pointer, optional ExceptionSource pointer and opt...
Definition: geventstate.h:131
GNet::FutureEventHandler
A callback interface for GNet::FutureEvent.
Definition: gfutureevent.h:126
GNet::FutureEvent::FutureEvent
FutureEvent(FutureEventHandler &, EventState)
Constructor. Installs itself in the event loop.
Definition: gfutureevent_unix.cpp:150
GNet::FutureEvent::send
static bool send(HANDLE handle, bool close=true) noexcept
Pokes an event into the main event loop so that the FutureEventHandler callback is called asynchronou...
Definition: gfutureevent_unix.cpp:158
GNet::FutureEvent::handle
HANDLE handle() noexcept
Extracts a handle that can be passed between threads and used in send().
Definition: gfutureevent_unix.cpp:163
GNet::FutureEvent::~FutureEvent
~FutureEvent()
Destructor.
G::Msg::send
static ssize_t send(SOCKET, const void *, std::size_t, int flags) noexcept
A send() wrapper.
Definition: gmsg_mac.cpp:27
G::Process::strerror
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
Definition: gprocess_unix.cpp:142
G::Process::errno_
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
Definition: gprocess_unix.cpp:125
geventloop.h
gfutureevent.h
gmsg.h
gprocess.h
std
STL namespace.