Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
OSSelectWakeup.hxx
Go to the documentation of this file.
1
34#ifndef _OS_OSSELECTWAKEUP_HXX_
35#define _OS_OSSELECTWAKEUP_HXX_
36
37#include <unistd.h>
38
39#include "openmrn_features.h"
40#include "utils/Atomic.hxx"
41#include "os/os.h"
42
43#if OPENMRN_FEATURE_DEVICE_SELECT
44#include "Devtab.hxx"
45#endif
46
47#if OPENMRN_HAVE_PSELECT
48#include <signal.h>
49#endif
50
51#ifdef __WINNT__
52#include <winsock2.h>
53#elif OPENMRN_HAVE_SELECT
54#include <sys/select.h>
55#endif
56
57#ifdef ESP_PLATFORM
58#include "sdkconfig.h"
59
60#ifdef CONFIG_VFS_SUPPORT_TERMIOS
61// remove defines added by arduino-esp32 core/esp32/binary.h which are
62// duplicated in sys/termios.h which may be included by esp_vfs.h
63#undef B110
64#undef B1000000
65#endif // CONFIG_VFS_SUPPORT_TERMIOS
66
67#include <esp_vfs.h>
68
69#endif // ESP_PLATFORM
70
72void empty_signal_handler(int sig);
73
75class OSSelectWakeup : private Atomic
76{
77public:
79 : pendingWakeup_(false)
80 , inSelect_(false)
81 {
82 }
83
85 {
86#ifdef ESP_PLATFORM
87 esp_deallocate_vfs_fd();
88#endif
89 }
90
92 os_thread_t main_thread()
93 {
94 return thread_;
95 }
96
100 {
101 // Gets the current thread.
103#ifdef ESP_PLATFORM
104 esp_allocate_vfs_fd();
105#endif
106#if OPENMRN_FEATURE_DEVICE_SELECT
107 Device::select_insert(&selectInfo_);
108#elif OPENMRN_HAVE_PSELECT
109 // Blocks SIGUSR1 in the signal mask of the current thread.
110 sigset_t usrmask;
111 HASSERT(!sigemptyset(&usrmask));
112 HASSERT(!sigaddset(&usrmask, WAKEUP_SIG));
113 HASSERT(!sigprocmask(SIG_BLOCK, &usrmask, &origMask_));
114 HASSERT(!sigdelset(&origMask_, WAKEUP_SIG));
115 struct sigaction action;
116 action.sa_handler = &empty_signal_handler;
117 HASSERT(!sigemptyset(&action.sa_mask));
118 action.sa_flags = 0;
119 HASSERT(!sigaction(WAKEUP_SIG, &action, nullptr));
120#endif
121 }
122
124 void wakeup()
125 {
126 bool need_wakeup = false;
127 {
128 AtomicHolder l(this);
129 pendingWakeup_ = true;
130 if (inSelect_)
131 {
132 need_wakeup = true;
133 }
134 }
135 if (need_wakeup)
136 {
137#if OPENMRN_FEATURE_DEVICE_SELECT
138 HASSERT(selectInfo_.event);
139 // We cannot destroy the thread ID in the local object.
140 Device::SelectInfo copy(selectInfo_);
142#elif OPENMRN_HAVE_PSELECT
143 pthread_kill(thread_, WAKEUP_SIG);
144#elif defined(ESP_PLATFORM)
145 esp_wakeup();
146#elif !defined(OPENMRN_FEATURE_SINGLE_THREADED)
147 DIE("need wakeup code");
148#endif
149 }
150 }
151
155 {
156 pendingWakeup_ = false;
157 }
158
159#if OPENMRN_FEATURE_RTOS_FROM_ISR
160 void wakeup_from_isr()
161 {
162#if defined(ESP_PLATFORM)
163 // On multi-core ESP32s we need to lock objects even in ISRs.
164 AtomicHolder h(this);
165#endif
166 pendingWakeup_ = true;
167 if (inSelect_)
168 {
169#if OPENMRN_FEATURE_DEVICE_SELECT
170 HASSERT(selectInfo_.event);
171 Device::SelectInfo copy(selectInfo_);
172 int woken;
173 Device::select_wakeup_from_isr(&copy, &woken);
174 os_isr_exit_yield_test(woken);
175// TODO: confirm if pthread_kill is ISR safe
176//#elif OPENMRN_HAVE_PSELECT
177// pthread_kill(thread_, WAKEUP_SIG);
178#elif defined(ESP_PLATFORM)
179 esp_wakeup_from_isr();
180#else
181 DIE("need wakeup code");
182#endif
183 }
184 }
185#endif // OPENMRN_FEATURE_RTOS_FROM_ISR
186
201 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
202 long long deadline_nsec);
203
204private:
205#ifdef ESP_PLATFORM
206 void esp_allocate_vfs_fd();
207 void esp_deallocate_vfs_fd();
208 void esp_wakeup();
209 void esp_wakeup_from_isr();
210public:
211 void esp_start_select(fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
212 esp_vfs_select_sem_t signal_sem);
213 void esp_end_select();
214
215private:
217 int vfsFd_{-1};
218
221 esp_vfs_select_sem_t espSem_;
222
225 fd_set *exceptFds_;
226
230 fd_set exceptFdsOrig_;
231
232#endif // ESP_PLATFORM
233
234#if OPENMRN_HAVE_PSELECT
236 static const int WAKEUP_SIG = SIGUSR1;
237#endif
243 os_thread_t thread_;
244#if OPENMRN_FEATURE_DEVICE_SELECT
245 Device::SelectInfo selectInfo_;
246#endif
247#if OPENMRN_HAVE_PSELECT
250 sigset_t origMask_;
251#endif
252};
253
254#endif // _OS_OSSELECTWAKEUP_HXX_
void empty_signal_handler(int sig)
Signal handler that does nothing.
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
Lightweight locking class for protecting small critical sections.
Definition Atomic.hxx:130
Helper class that allows a select to be asynchronously woken up.
void wakeup()
Wakes up the select in the locked thread.
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, long long deadline_nsec)
Portable call to a select that can be woken up asynchronously from a different thread or an ISR conte...
os_thread_t thread_
ID of the main thread we are engaged upon.
bool pendingWakeup_
True if there was a wakeup call since the previous select finished.
os_thread_t main_thread()
bool inSelect_
True during the duration of a select operation.
void clear_wakeup()
Called from the main thread after being woken up.
void lock_to_thread()
Prepares the current thread for asynchronous wakeups.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
OS_INLINE os_thread_t os_thread_self(void)
Return a handle to the calling thread.
Definition os.h:370
Select wakeup information.
Definition Devtab.hxx:491
static void select_wakeup_from_isr(SelectInfo *info, int *woken)
Wakeup the list of clients needing woken.
Definition Select.cxx:224
static void select_insert(SelectInfo *info)
Add client to list of clients needing woken.
Definition Select.cxx:197
static void select_wakeup(SelectInfo *info)
Wakeup the list of clients needing woken.
Definition Select.cxx:210