• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenLightingProject / ola / 20179851591

12 Dec 2025 09:05PM UTC coverage: 45.048% (-0.7%) from 45.72%
20179851591

Pull #2027

github

web-flow
Bump actions/upload-artifact from 4 to 6

Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #2027: Bump actions/upload-artifact from 4 to 6

8554 of 19812 branches covered (43.18%)

22094 of 49046 relevant lines covered (45.05%)

50.63 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.43
/common/io/SelectPoller.cpp
1
/*
2
 * This library is free software; you can redistribute it and/or
3
 * modify it under the terms of the GNU Lesser General Public
4
 * License as published by the Free Software Foundation; either
5
 * version 2.1 of the License, or (at your option) any later version.
6
 *
7
 * This library is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10
 * Lesser General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU Lesser General Public
13
 * License along with this library; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
 *
16
 * SelectPoller.cpp
17
 * A Poller which uses select()
18
 * Copyright (C) 2013 Simon Newton
19
 */
20

21
#ifdef _WIN32
22
// Pull in fd_set and related definitions.
23
#include <ola/win/CleanWinSock2.h>
24
#endif  // _WIN32
25

26
#include "common/io/SelectPoller.h"
27

28
#include <string.h>
29
#include <errno.h>
30

31
#include <algorithm>
32
#include <map>
33
#include <queue>
34
#include <string>
35
#include <utility>
36

37
#include "ola/Clock.h"
38
#include "ola/Logging.h"
39
#include "ola/base/Macro.h"
40
#include "ola/io/Descriptor.h"
41
#include "ola/stl/STLUtils.h"
42

43
namespace ola {
44
namespace io {
45

46
using std::pair;
47
using std::map;
48
using std::string;
49

50
/**
51
 * @brief Insert a descriptor into one of the descriptor maps.
52
 * @param descriptor_map the descriptor_map to insert into.
53
 * @param fd the FD to use as the key
54
 * @param value the value to associate with the key
55
 * @param type the name of the map, used for logging if the fd already exists
56
 *   in the map.
57
 * @returns true if the descriptor was inserted, false if it was already in the
58
 *   map.
59
 *
60
 * There are three possibilities:
61
 *  - The fd does not already exist in the map
62
 *  - The fd exists and the value is NULL.
63
 *  - The fd exists and is not NULL.
64
 */
65
template <typename T>
66
bool InsertIntoDescriptorMap(map<int, T*> *descriptor_map, int fd, T *value,
58✔
67
                             const string &type) {
68
  typedef map<int, T*> MapType;
69
  pair<typename MapType::iterator, bool> p = descriptor_map->insert(
58✔
70
      typename MapType::value_type(fd, value));
58✔
71

72
  if (!p.second) {
58✔
73
    // already in map
74
    if (p.first->second == NULL) {
×
75
      p.first->second = value;
×
76
    } else {
77
      OLA_WARN << "FD " << fd << " was already in the " << type
×
78
          << " descriptor map: " << p.first->second << " : " << value;
×
79
      return false;
×
80
    }
81
  }
82
  return true;
83
}
84

85
/**
86
 * @brief Remove a FD from a descriptor map by setting the value to NULL.
87
 * @returns true if the FD was removed from the map, false if it didn't exist
88
 *   in the map.
89
 */
90
template <typename T>
91
bool RemoveFromDescriptorMap(map<int, T*> *descriptor_map, int fd) {
75✔
92
  T **ptr = STLFind(descriptor_map, fd);
136✔
93
  if (ptr) {
94
    *ptr = NULL;
61✔
95
    return true;
61✔
96
  }
97
  return false;
98
}
99

100
SelectPoller::SelectPoller(ExportMap *export_map, Clock* clock)
175✔
101
    : m_export_map(export_map),
175✔
102
      m_loop_iterations(NULL),
175✔
103
      m_loop_time(NULL),
175✔
104
      m_clock(clock) {
175✔
105
  if (m_export_map) {
175✔
106
    m_loop_time = m_export_map->GetCounterVar(K_LOOP_TIME);
15✔
107
    m_loop_iterations = m_export_map->GetCounterVar(K_LOOP_COUNT);
15✔
108
  }
109
}
175✔
110

111
SelectPoller::~SelectPoller() {
175✔
112
  ConnectedDescriptorMap::iterator iter = m_connected_read_descriptors.begin();
175✔
113
  for (; iter != m_connected_read_descriptors.end(); ++iter) {
500✔
114
    if (iter->second) {
325✔
115
      if (iter->second->delete_on_close) {
188✔
116
        delete iter->second->descriptor;
2✔
117
      }
118
      delete iter->second;
188✔
119
    }
120
  }
121
  m_read_descriptors.clear();
175✔
122
  m_connected_read_descriptors.clear();
175✔
123
  m_write_descriptors.clear();
175✔
124
}
175✔
125

126
bool SelectPoller::AddReadDescriptor(class ReadFileDescriptor *descriptor) {
58✔
127
  if (!descriptor->ValidReadDescriptor()) {
58✔
128
    OLA_WARN << "AddReadDescriptor called with invalid descriptor";
×
129
    return false;
×
130
  }
131

132
  return InsertIntoDescriptorMap(&m_read_descriptors,
116✔
133
      descriptor->ReadDescriptor(), descriptor, "read");
58✔
134
}
135

136
bool SelectPoller::AddReadDescriptor(class ConnectedDescriptor *descriptor,
383✔
137
                                     bool delete_on_close) {
138
  if (!descriptor->ValidReadDescriptor()) {
383✔
139
    OLA_WARN << "AddReadDescriptor called with invalid descriptor";
1✔
140
    return false;
1✔
141
  }
142

143
  connected_descriptor_t *cd = new connected_descriptor_t();
382✔
144
  cd->descriptor = descriptor;
382✔
145
  cd->delete_on_close = delete_on_close;
382✔
146

147
  bool ok = InsertIntoDescriptorMap(&m_connected_read_descriptors,
764✔
148
      descriptor->ReadDescriptor(), cd, "connected");
382✔
149
  if (!ok) {
382✔
150
    delete cd;
×
151
  }
152
  return ok;
153
}
154

155
bool SelectPoller::RemoveReadDescriptor(class ReadFileDescriptor *descriptor) {
75✔
156
  if (!descriptor->ValidReadDescriptor()) {
75✔
157
    OLA_WARN << "Removing an invalid ReadDescriptor";
×
158
    return false;
×
159
  }
160

161
  return RemoveFromDescriptorMap(&m_read_descriptors,
75✔
162
                                 descriptor->ReadDescriptor());
150✔
163
}
164

165
bool SelectPoller::RemoveReadDescriptor(class ConnectedDescriptor *descriptor) {
181✔
166
  if (!descriptor->ValidReadDescriptor()) {
181✔
167
    OLA_WARN << "Removing an invalid ConnectedDescriptor";
×
168
    return false;
×
169
  }
170

171
  connected_descriptor_t **ptr = STLFind(
543✔
172
      &m_connected_read_descriptors, descriptor->ReadDescriptor());
181✔
173
  if (ptr && *ptr) {
181✔
174
    delete *ptr;
158✔
175
    *ptr = NULL;
158✔
176
    return true;
158✔
177
  }
178
  return false;
179
}
180

181
bool SelectPoller::AddWriteDescriptor(class WriteFileDescriptor *descriptor) {
20✔
182
  if (!descriptor->ValidWriteDescriptor()) {
20✔
183
    OLA_WARN << "AddWriteDescriptor called with invalid descriptor";
1✔
184
    return false;
1✔
185
  }
186

187
  return InsertIntoDescriptorMap(&m_write_descriptors,
38✔
188
      descriptor->WriteDescriptor(), descriptor, "write");
19✔
189
}
190

191
bool SelectPoller::RemoveWriteDescriptor(
19✔
192
    class WriteFileDescriptor *descriptor) {
193
  if (!descriptor->ValidWriteDescriptor()) {
19✔
194
    OLA_WARN << "Removing an invalid WriteDescriptor";
×
195
    return false;
×
196
  }
197

198
  return RemoveFromDescriptorMap(&m_write_descriptors,
19✔
199
                                 descriptor->WriteDescriptor());
38✔
200
}
201

202
bool SelectPoller::Poll(TimeoutManager *timeout_manager,
919✔
203
                        const TimeInterval &poll_interval) {
204
  int maxsd;
919✔
205
  fd_set r_fds, w_fds;
919✔
206
  TimeStamp now;
919✔
207
  TimeInterval sleep_interval = poll_interval;
919✔
208
  struct timeval tv;
919✔
209

210
  maxsd = 0;
919✔
211
  FD_ZERO(&r_fds);
15,623✔
212
  FD_ZERO(&w_fds);
15,623✔
213
  m_clock->CurrentMonotonicTime(&now);
919✔
214

215
  TimeInterval next_event_in = timeout_manager->ExecuteTimeouts(&now);
919✔
216
  if (!next_event_in.IsZero()) {
919✔
217
    sleep_interval = std::min(next_event_in, sleep_interval);
1,374✔
218
  }
219

220
  // Adding descriptors should be the last thing we do, they may have changed
221
  // due to timeouts above.
222
  bool closed_descriptors = AddDescriptorsToSet(&r_fds, &w_fds, &maxsd);
919✔
223
  // If there are closed descriptors, set the timeout to something
224
  // very small (1ms). This ensures we at least make a pass through the
225
  // descriptors.
226
  if (closed_descriptors) {
919✔
227
    sleep_interval = std::min(sleep_interval, TimeInterval(0, 1000));
6✔
228
  }
229

230
  // take care of stats accounting
231
  if (m_wake_up_time.IsSet()) {
919✔
232
    TimeInterval loop_time = now - m_wake_up_time;
766✔
233
    OLA_DEBUG << "ss process time was " << loop_time.ToString();
766✔
234
    if (m_loop_time)
766✔
235
      (*m_loop_time) += loop_time.AsInt();
33✔
236
    if (m_loop_iterations)
766✔
237
      (*m_loop_iterations)++;
33✔
238
  }
239

240
  sleep_interval.AsTimeval(&tv);
919✔
241
  switch (select(maxsd + 1, &r_fds, &w_fds, NULL, &tv)) {
919✔
242
    case 0:
178✔
243
      // timeout
244
      m_clock->CurrentMonotonicTime(&m_wake_up_time);
178✔
245
      timeout_manager->ExecuteTimeouts(&m_wake_up_time);
178✔
246

247
      if (closed_descriptors) {
178✔
248
        // there were closed descriptors before the select() we need to deal
249
        // with them.
250
        FD_ZERO(&r_fds);
×
251
        FD_ZERO(&w_fds);
×
252
        CheckDescriptors(&r_fds, &w_fds);
×
253
      }
254
      return true;
255
    case -1:
×
256
      if (errno == EINTR)
×
257
        return true;
258
      OLA_WARN << "select() error, " << strerror(errno);
×
259
      return false;
×
260
    default:
741✔
261
      m_clock->CurrentMonotonicTime(&m_wake_up_time);
741✔
262
      CheckDescriptors(&r_fds, &w_fds);
741✔
263
      m_clock->CurrentMonotonicTime(&m_wake_up_time);
741✔
264
      timeout_manager->ExecuteTimeouts(&m_wake_up_time);
741✔
265
  }
266

267
  return true;
741✔
268
}
269

270
/*
271
 * Add all the descriptors to the FD_SET.
272
 * @returns true if there are descriptors that have been closed.
273
 *
274
 * This also takes care of removing any entries from the maps where the value
275
 * is NULL. This is safe because we don't execute any callbacks from within
276
 * this method.
277
 */
278
bool SelectPoller::AddDescriptorsToSet(fd_set *r_set,
919✔
279
                                       fd_set *w_set,
280
                                       int *max_sd) {
281
  bool closed_descriptors = false;
919✔
282

283
  ReadDescriptorMap::iterator iter = m_read_descriptors.begin();
919✔
284
  while (iter != m_read_descriptors.end()) {
1,074✔
285
    ReadDescriptorMap::iterator this_iter = iter;
155✔
286
    iter++;
155✔
287

288
    ReadFileDescriptor *descriptor = this_iter->second;
155✔
289
    if (!descriptor) {
155✔
290
      // This one was removed.
291
      m_read_descriptors.erase(this_iter);
15✔
292
      continue;
15✔
293
    }
294

295
    if (descriptor->ValidReadDescriptor()) {
140✔
296
      *max_sd = std::max(*max_sd, descriptor->ReadDescriptor());
140✔
297
      FD_SET(descriptor->ReadDescriptor(), r_set);
140✔
298
    } else {
299
      // The descriptor was probably closed without removing it from the select
300
      // server
301
      if (m_export_map) {
×
302
        (*m_export_map->GetIntegerVar(K_READ_DESCRIPTOR_VAR))--;
×
303
      }
304
      m_read_descriptors.erase(this_iter);
×
305
      OLA_WARN << "Removed a inactive descriptor from the select server";
×
306
    }
307
  }
308

309
  ConnectedDescriptorMap::iterator con_iter =
919✔
310
      m_connected_read_descriptors.begin();
919✔
311
  while (con_iter != m_connected_read_descriptors.end()) {
3,106✔
312
    ConnectedDescriptorMap::iterator this_iter = con_iter;
2,187✔
313
    con_iter++;
2,187✔
314

315
    if (!this_iter->second) {
2,187✔
316
      // This one was removed.
317
      m_connected_read_descriptors.erase(this_iter);
55✔
318
      continue;
55✔
319
    }
320

321
    if (this_iter->second->descriptor->ValidReadDescriptor()) {
2,132✔
322
      *max_sd = std::max(
2,129✔
323
          *max_sd, this_iter->second->descriptor->ReadDescriptor());
2,129✔
324
      FD_SET(this_iter->second->descriptor->ReadDescriptor(), r_set);
2,129✔
325
    } else {
326
      closed_descriptors = true;
327
    }
328
  }
329

330
  WriteDescriptorMap::iterator write_iter = m_write_descriptors.begin();
919✔
331
  while (write_iter != m_write_descriptors.end()) {
946✔
332
    WriteDescriptorMap::iterator this_iter = write_iter;
27✔
333
    write_iter++;
27✔
334

335
    WriteFileDescriptor *descriptor = this_iter->second;
27✔
336
    if (!descriptor) {
27✔
337
      // This one was removed.
338
      m_write_descriptors.erase(this_iter);
13✔
339
      continue;
13✔
340
    }
341

342
    if (descriptor->ValidWriteDescriptor()) {
14✔
343
      *max_sd = std::max(*max_sd, descriptor->WriteDescriptor());
14✔
344
      FD_SET(descriptor->WriteDescriptor(), w_set);
14✔
345
    } else {
346
      // The descriptor was probably closed without removing it from the select
347
      // server
348
      if (m_export_map) {
×
349
        (*m_export_map->GetIntegerVar(K_WRITE_DESCRIPTOR_VAR))--;
×
350
      }
351
      m_write_descriptors.erase(this_iter);
×
352
      OLA_WARN << "Removed a disconnected descriptor from the select server";
×
353
    }
354
  }
355
  return closed_descriptors;
919✔
356
}
357

358

359
/*
360
 * Check all the registered descriptors:
361
 *  - Execute the callback for descriptors with data
362
 *  - Execute OnClose if a remote end closed the connection
363
 */
364
void SelectPoller::CheckDescriptors(fd_set *r_set, fd_set *w_set) {
741✔
365
  // Remember the add / remove methods above may be called during
366
  // PerformRead(), PerformWrite() or the on close handler. Our iterators are
367
  // safe because we only ever call erase from within AddDescriptorsToSet(),
368
  // which isn't called from any of the Add / Remove methods.
369
  ReadDescriptorMap::iterator iter = m_read_descriptors.begin();
741✔
370
  for (; iter != m_read_descriptors.end(); ++iter) {
880✔
371
    if (iter->second && FD_ISSET(iter->second->ReadDescriptor(), r_set)) {
139✔
372
      iter->second->PerformRead();
29✔
373
    }
374
  }
375

376
  ConnectedDescriptorMap::iterator con_iter =
741✔
377
      m_connected_read_descriptors.begin();
741✔
378
  for (; con_iter != m_connected_read_descriptors.end(); ++con_iter) {
2,583✔
379
    if (!con_iter->second) {
1,842✔
380
      continue;
18✔
381
    }
382

383
    connected_descriptor_t *cd = con_iter->second;
1,824✔
384
    ConnectedDescriptor *descriptor = cd->descriptor;
1,824✔
385

386
    bool closed = false;
1,824✔
387
    if (!descriptor->ValidReadDescriptor()) {
1,824✔
388
      closed = true;
389
    } else if (FD_ISSET(descriptor->ReadDescriptor(), r_set)) {
1,821✔
390
      if (descriptor->IsClosed()) {
728✔
391
        closed = true;
392
      } else {
393
        descriptor->PerformRead();
695✔
394
      }
395
    }
396

397
    if (closed) {
398
      ConnectedDescriptor::OnCloseCallback *on_close =
36✔
399
        descriptor->TransferOnClose();
36✔
400
      bool delete_on_close = cd->delete_on_close;
36✔
401

402
      delete con_iter->second;
36✔
403
      con_iter->second = NULL;
36✔
404
      if (m_export_map) {
36✔
405
        (*m_export_map->GetIntegerVar(K_CONNECTED_DESCRIPTORS_VAR))--;
5✔
406
      }
407

408
      if (on_close)
36✔
409
        on_close->Run();
25✔
410

411
      if (delete_on_close)
36✔
412
        delete descriptor;
4✔
413
    }
414
  }
415

416
  // Check the write sockets. These may have changed since the start of the
417
  // method due to running callbacks.
418
  WriteDescriptorMap::iterator write_iter = m_write_descriptors.begin();
741✔
419
  for (; write_iter != m_write_descriptors.end(); write_iter++) {
755✔
420
    if (write_iter->second &&
14✔
421
        FD_ISSET(write_iter->second->WriteDescriptor(), w_set)) {
11✔
422
      write_iter->second->PerformWrite();
11✔
423
    }
424
  }
425
}
741✔
426
}  // namespace io
427
}  // namespace ola
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc