Alexandria  2.25.0
SDC-CH common library for the Euclid project
SemaphoreMach.icpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012-2021 Euclid Science Ground Segment
3  *
4  * This library is free software; you can redistribute it and/or modify it under
5  * the terms of the GNU Lesser General Public License as published by the Free
6  * Software Foundation; either version 3.0 of the License, or (at your option)
7  * any later version.
8  *
9  * This library is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <cerrno>
20 #include <mach/mach_init.h>
21 #include <mach/mach_traps.h>
22 #include <mach/semaphore.h>
23 #include <mach/task.h>
24 #include <system_error>
25 
26 namespace Euclid {
27 class Semaphore::SemaphoreImpl {
28 public:
29  explicit SemaphoreImpl(unsigned int i) {
30  auto ret = semaphore_create(mach_task_self(), &m_semaphore, SYNC_POLICY_FIFO, i);
31  if (ret != KERN_SUCCESS) {
32  throw std::system_error(errno, std::system_category());
33  }
34  }
35 
36  ~SemaphoreImpl() {
37  semaphore_destroy(mach_task_self(), m_semaphore);
38  }
39 
40  void post() {
41  if (semaphore_signal(m_semaphore) != KERN_SUCCESS) {
42  throw std::system_error(EINVAL, std::system_category());
43  }
44  }
45 
46  void wait() {
47  for (;;) {
48  auto ret = semaphore_wait(m_semaphore);
49  if (ret == KERN_SUCCESS) {
50  return;
51  }
52  if (ret == KERN_ABORTED) {
53  continue;
54  }
55  throw std::system_error(EINVAL, std::system_category());
56  }
57  }
58 
59  bool try_acquire() {
60  const mach_timespec_t wait{0, 0};
61  return timed_wait(wait);
62  }
63 
64  bool try_acquire_until(std::chrono::system_clock::time_point abs_time) {
65  using std::chrono::duration_cast;
66 
67  auto now = std::chrono::system_clock::now();
68  auto left = abs_time - now;
69 
70  auto seconds = duration_cast<std::chrono::seconds>(left);
71  auto nseconds = duration_cast<std::chrono::nanoseconds>(left) - duration_cast<std::chrono::nanoseconds>(seconds);
72 
73  const mach_timespec_t wait{static_cast<unsigned int>(seconds.count()), static_cast<int>(nseconds.count())};
74  return timed_wait(wait);
75  }
76 
77 private:
78  semaphore_t m_semaphore;
79 
80  bool timed_wait(const mach_timespec_t& wait) {
81  auto ret = semaphore_timedwait(m_semaphore, wait);
82  if (ret == KERN_SUCCESS) {
83  return true;
84  }
85  if (ret == KERN_OPERATION_TIMED_OUT) {
86  return false;
87  }
88  throw std::system_error(EINVAL, std::system_category());
89  }
90 };
91 } // namespace Euclid