SHM
Shared-memory based Handy-communication Manager
shm_action.hpp
Go to the documentation of this file.
1 
13 #ifndef __SHM_ACTION_LIB_H__
14 #define __SHM_ACTION_LIB_H__
15 
16 #include <string>
17 #include <thread>
18 #include "shm_base.hpp"
19 
20 namespace irlab
21 {
22 
23 namespace shm
24 {
25 
26 enum ACTION_STATUS : uint8_t
27 {
28  ACTIVE,
29  REJECTED,
30  SUCCEEDED,
31  PREEMPTED,
32 };
33 
34 // ****************************************************************************
46 // ****************************************************************************
47 template <class Goal, class Result, class Feedback>
49 {
50 public:
51  ActionServer(std::string name, PERM perm = DEFAULT_PERM);
52  ~ActionServer();
53 
54  void waitNewGoalAvailable();
55  Goal acceptNewGoal();
56  void rejectNewGoal();
57 
58  bool isPreemptRequested();
59  void setPreempted();
60 
61  void publishResult(const Result& result);
62  void publishFeedback(const Feedback& feedback);
63 
64 private:
65  void initializeExclusiveAccess();
66 
67  pthread_t thread;
68 
69  std::string shm_name;
70  PERM shm_perm;
71  SharedMemory *shared_memory;
72 
73  uint64_t start_timestamp_us;
74 
75  uint8_t *memory_ptr;
76 
77  pthread_mutex_t *goal_mutex;
78  pthread_cond_t *goal_condition;
79  uint64_t *goal_timestamp_us;
80  Goal *goal_ptr;
81  pthread_mutex_t *result_mutex;
82  pthread_cond_t *result_condition;
83  uint64_t *result_timestamp_us;
84  Result *result_ptr;
85  Feedback *feedback_ptr;
86  ACTION_STATUS *status_ptr;
87  uint64_t *cancel_timestamp_us;
88 
89  uint64_t current_goal_timestamp_usec;
90 };
91 
92 // ****************************************************************************
97 // ****************************************************************************
98 template <class Goal, class Result, class Feedback>
100 {
101 public:
102  ActionClient(std::string name);
103  ~ActionClient();
104 
105  void cancelGoal();
106  Result getResult();
107  Feedback getFeedback();
108  ACTION_STATUS getStatus();
109  bool isServerConnected();
110  bool sendGoal(Goal goal);
111  bool waitForResult(unsigned long wait_time_us);
112  bool waitForServer(unsigned long wait_time_us);
113 
114 private:
115  std::string shm_name;
116  SharedMemory *shared_memory;
117 
118  uint8_t *memory_ptr;
119 
120  pthread_mutex_t *goal_mutex;
121  pthread_cond_t *goal_condition;
122  uint64_t *goal_timestamp_us;
123  Goal *goal_ptr;
124  pthread_mutex_t *result_mutex;
125  pthread_cond_t *result_condition;
126  uint64_t *result_timestamp_us;
127  Result *result_ptr;
128  Feedback *feedback_ptr;
129  ACTION_STATUS *status_ptr;
130  uint64_t *cancel_timestamp_us;
131 
132  uint64_t current_result_timestamp_usec;
133 };
134 
135 // ****************************************************************************
136 // 関数定義
137 // (テンプレートクラス内の関数の定義はコンパイル時に実体化するのでヘッダに書く)
138 // ****************************************************************************
139 template <class Goal, class Result, class Feedback>
141 : shm_name(name)
142 , shm_perm(perm)
143 , shared_memory(nullptr)
144 , memory_ptr(nullptr)
145 {
146  if (!std::is_standard_layout<Goal>::value
147  || !std::is_standard_layout<Result>::value
148  || !std::is_standard_layout<Feedback>::value)
149  {
150  throw std::runtime_error("shm::ActionServer: Be setted not POD class!");
151  }
152 
153  shared_memory = new SharedMemoryPosix(shm_name, O_RDWR|O_CREAT, shm_perm);
154  shared_memory->connect( (sizeof(pthread_mutex_t)+sizeof(pthread_cond_t)+sizeof(uint64_t)) * 2 + sizeof(Goal) + sizeof(Result) + sizeof(Feedback) + sizeof(ACTION_STATUS) + sizeof(uint64_t));
155  if (shared_memory->isDisconnected())
156  {
157  throw std::runtime_error("shm::ActionServer: Cannot get memory!");
158  }
159 
160  uint8_t *data_ptr = shared_memory->getPtr();
161  memory_ptr = data_ptr;
162  goal_mutex = reinterpret_cast<pthread_mutex_t *>(data_ptr);
163  data_ptr += sizeof(pthread_mutex_t);
164  goal_condition = reinterpret_cast<pthread_cond_t *>(data_ptr);
165  data_ptr += sizeof(pthread_cond_t);
166  goal_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
167  data_ptr += sizeof(uint64_t);
168  goal_ptr = reinterpret_cast<Goal *>(data_ptr);
169  data_ptr += sizeof(Goal);
170  result_mutex = reinterpret_cast<pthread_mutex_t *>(data_ptr);
171  data_ptr += sizeof(pthread_mutex_t);
172  result_condition = reinterpret_cast<pthread_cond_t *>(data_ptr);
173  data_ptr += sizeof(pthread_cond_t);
174  result_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
175  data_ptr += sizeof(uint64_t);
176  result_ptr = reinterpret_cast<Result *>(data_ptr);
177  data_ptr += sizeof(Result);
178  feedback_ptr = reinterpret_cast<Feedback *>(data_ptr);
179  data_ptr += sizeof(Feedback);
180  status_ptr = reinterpret_cast<ACTION_STATUS *>(data_ptr);
181  data_ptr += sizeof(ACTION_STATUS);
182  cancel_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
183 
184  initializeExclusiveAccess();
185 
186  *status_ptr = SUCCEEDED;
187 
188  struct timespec ts;
189  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
190  *cancel_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
191  start_timestamp_us = *cancel_timestamp_us;
192  *goal_timestamp_us = *cancel_timestamp_us;
193  *result_timestamp_us = *cancel_timestamp_us;
194 
195  current_goal_timestamp_usec = *goal_timestamp_us;
196 }
197 
198 template <class Goal, class Result, class Feedback>
199 ActionServer<Goal, Result, Feedback>::~ActionServer()
200 {
201  shared_memory->disconnect();
202  if (shared_memory != nullptr)
203  {
204  delete shared_memory;
205  }
206 }
207 
208 template <class Goal, class Result, class Feedback>
209 void
210 ActionServer<Goal, Result, Feedback>::initializeExclusiveAccess()
211 {
212  pthread_condattr_t goal_cond_attr;
213  pthread_condattr_init(&goal_cond_attr);
214  pthread_condattr_setpshared(&goal_cond_attr, PTHREAD_PROCESS_SHARED);
215  pthread_cond_init(goal_condition, &goal_cond_attr);
216  pthread_condattr_destroy(&goal_cond_attr);
217 
218  pthread_mutexattr_t goal_m_attr;
219  pthread_mutexattr_init(&goal_m_attr);
220  pthread_mutexattr_setpshared(&goal_m_attr, PTHREAD_PROCESS_SHARED);
221  pthread_mutex_init(goal_mutex, &goal_m_attr);
222  pthread_mutexattr_destroy(&goal_m_attr);
223 
224  pthread_condattr_t result_cond_attr;
225  pthread_condattr_init(&result_cond_attr);
226  pthread_condattr_setpshared(&result_cond_attr, PTHREAD_PROCESS_SHARED);
227  pthread_cond_init(result_condition, &result_cond_attr);
228  pthread_condattr_destroy(&result_cond_attr);
229 
230  pthread_mutexattr_t result_m_attr;
231  pthread_mutexattr_init(&result_m_attr);
232  pthread_mutexattr_setpshared(&result_m_attr, PTHREAD_PROCESS_SHARED);
233  pthread_mutex_init(result_mutex, &result_m_attr);
234  pthread_mutexattr_destroy(&result_m_attr);
235 }
236 
237 template <class Goal, class Result, class Feedback>
238 void
239 ActionServer<Goal, Result, Feedback>::waitNewGoalAvailable()
240 {
241  // Fix race condition: Check timestamp inside mutex
242  pthread_mutex_lock(goal_mutex);
243  while (current_goal_timestamp_usec >= *goal_timestamp_us)
244  {
245  // Wait on the condvar while holding the mutex
246  pthread_cond_wait(goal_condition, goal_mutex);
247  }
248  pthread_mutex_unlock(goal_mutex);
249 }
250 
251 template <class Goal, class Result, class Feedback>
252 Goal
253 ActionServer<Goal, Result, Feedback>::acceptNewGoal()
254 {
255  pthread_mutex_lock(goal_mutex);
256  *status_ptr = ACTIVE;
257  struct timespec ts;
258  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
259  start_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
260  current_goal_timestamp_usec = *goal_timestamp_us;
261  Goal goal_copy = *goal_ptr;
262  pthread_mutex_unlock(goal_mutex);
263 
264  return goal_copy;
265 }
266 
267 template <class Goal, class Result, class Feedback>
268 void
269 ActionServer<Goal, Result, Feedback>::rejectNewGoal()
270 {
271  *status_ptr = REJECTED;
272  current_goal_timestamp_usec = *goal_timestamp_us;
273  pthread_cond_broadcast(result_condition);
274 }
275 
276 template <class Goal, class Result, class Feedback>
277 bool
278 ActionServer<Goal, Result, Feedback>::isPreemptRequested()
279 {
280  if (start_timestamp_us < *cancel_timestamp_us)
281  {
282  return true;
283  }
284  return false;
285 }
286 
287 template <class Goal, class Result, class Feedback>
288 void
289 ActionServer<Goal, Result, Feedback>::setPreempted()
290 {
291  *status_ptr = PREEMPTED;
292 
293  struct timespec ts;
294  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
295  *result_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
296 
297  pthread_cond_broadcast(result_condition);
298 }
299 
300 template <class Goal, class Result, class Feedback>
301 void
302 ActionServer<Goal, Result, Feedback>::publishResult(const Result& result)
303 {
304  pthread_mutex_lock(result_mutex);
305  *result_ptr = result;
306  *status_ptr = SUCCEEDED;
307 
308  struct timespec ts;
309  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
310  *result_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
311  pthread_mutex_unlock(result_mutex);
312 
313  pthread_cond_broadcast(result_condition);
314 }
315 
316 template <class Goal, class Result, class Feedback>
317 void
318 ActionServer<Goal, Result, Feedback>::publishFeedback(const Feedback& feedback)
319 {
320  // Use result_mutex to protect feedback updates (reusing existing mutex)
321  pthread_mutex_lock(result_mutex);
322  *feedback_ptr = feedback;
323  pthread_mutex_unlock(result_mutex);
324 }
325 
326 
327 template <class Goal, class Result, class Feedback>
328 ActionClient<Goal, Result, Feedback>::ActionClient(std::string name)
329 : shm_name(name)
330 , shared_memory(nullptr)
331 {
332  if (!std::is_standard_layout<Goal>::value
333  || !std::is_standard_layout<Result>::value
334  || !std::is_standard_layout<Feedback>::value)
335  {
336  throw std::runtime_error("shm::ActionClient: Be setted not POD class!");
337  }
338  shared_memory = new SharedMemoryPosix(shm_name, O_RDWR, static_cast<PERM>(0));
339 }
340 
341 template <class Goal, class Result, class Feedback>
342 ActionClient<Goal, Result, Feedback>::~ActionClient()
343 {
344  if (shared_memory != nullptr)
345  {
346  delete shared_memory;
347  }
348 }
349 
350 template <class Goal, class Result, class Feedback>
351 bool
352 ActionClient<Goal, Result, Feedback>::isServerConnected()
353 {
354  // Check the action shared memory existance.
355  if (shared_memory->isDisconnected())
356  {
357  shared_memory->connect();
358  if (shared_memory->isDisconnected())
359  {
360  return false;
361  }
362  uint8_t *data_ptr = shared_memory->getPtr();
363  memory_ptr = data_ptr;
364  goal_mutex = reinterpret_cast<pthread_mutex_t *>(data_ptr);
365  data_ptr += sizeof(pthread_mutex_t);
366  goal_condition = reinterpret_cast<pthread_cond_t *>(data_ptr);
367  data_ptr += sizeof(pthread_cond_t);
368  goal_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
369  data_ptr += sizeof(uint64_t);
370  goal_ptr = reinterpret_cast<Goal *>(data_ptr);
371  data_ptr += sizeof(Goal);
372  result_mutex = reinterpret_cast<pthread_mutex_t *>(data_ptr);
373  data_ptr += sizeof(pthread_mutex_t);
374  result_condition = reinterpret_cast<pthread_cond_t *>(data_ptr);
375  data_ptr += sizeof(pthread_cond_t);
376  result_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
377  data_ptr += sizeof(uint64_t);
378  result_ptr = reinterpret_cast<Result *>(data_ptr);
379  data_ptr += sizeof(Result);
380  feedback_ptr = reinterpret_cast<Feedback *>(data_ptr);
381  data_ptr += sizeof(Feedback);
382  status_ptr = reinterpret_cast<ACTION_STATUS *>(data_ptr);
383  data_ptr += sizeof(ACTION_STATUS);
384  cancel_timestamp_us = reinterpret_cast<uint64_t *>(data_ptr);
385  }
386 
387  return true;
388 }
389 
390 template <class Goal, class Result, class Feedback>
391 bool
392 ActionClient<Goal, Result, Feedback>::sendGoal(Goal goal)
393 {
394  // Check the action shared memory existance.
395  if (!(isServerConnected()))
396  {
397  return false;
398  }
399 
400  current_result_timestamp_usec = *result_timestamp_us;
401 
402  // Set request to shared memory
403  *goal_ptr = goal;
404  struct timespec ts;
405  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
406  *goal_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
407  pthread_cond_broadcast(goal_condition);
408 
409  return true;
410 }
411 
412 template <class Goal, class Result, class Feedback>
413 Result
414 ActionClient<Goal, Result, Feedback>::getResult()
415 {
416  return *result_ptr;
417 }
418 
419 template <class Goal, class Result, class Feedback>
420 Feedback
421 ActionClient<Goal, Result, Feedback>::getFeedback()
422 {
423  pthread_mutex_lock(result_mutex);
424  Feedback feedback_copy = *feedback_ptr;
425  pthread_mutex_unlock(result_mutex);
426  return feedback_copy;
427 }
428 
429 template <class Goal, class Result, class Feedback>
430 ACTION_STATUS
431 ActionClient<Goal, Result, Feedback>::getStatus()
432 {
433  return *status_ptr;
434 }
435 
436 template <class Goal, class Result, class Feedback>
437 void
438 ActionClient<Goal, Result, Feedback>::cancelGoal()
439 {
440  struct timespec ts;
441  clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
442  *cancel_timestamp_us = ((uint64_t) ts.tv_sec * 1000000L) + ((uint64_t) ts.tv_nsec / 1000L);
443 }
444 
445 template <class Goal, class Result, class Feedback>
446 bool
447 ActionClient<Goal, Result, Feedback>::waitForResult(unsigned long wait_time_us)
448 {
449  struct timespec ts;
450  long sec = static_cast<long>(wait_time_us / 1000000);
451  long mod_sec = static_cast<long>(wait_time_us % 1000000);
452  clock_gettime(CLOCK_REALTIME, &ts);
453  ts.tv_sec += sec;
454  ts.tv_nsec+= mod_sec * 1000;
455  if (1000000000 <= ts.tv_nsec)
456  {
457  ts.tv_nsec -= 1000000000;
458  ts.tv_sec += 1;
459  }
460 
461  while (current_result_timestamp_usec >= *result_timestamp_us)
462  {
463  // Wait on the condvar
464  pthread_mutex_lock(result_mutex);
465  int ret = pthread_cond_timedwait(result_condition, result_mutex, &ts);
466  pthread_mutex_unlock(result_mutex);
467  if (ret == ETIMEDOUT)
468  {
469  return false;
470  }
471  }
472  return true;
473 }
474 
475 template <class Goal, class Result, class Feedback>
476 bool
477 ActionClient<Goal, Result, Feedback>::waitForServer(uint64_t wait_time_us)
478 {
479  static const uint64_t SLEEP_PERIOD_us = 100000;
480 
481  if (isServerConnected())
482  {
483  return true;
484  }
485 
486  for (uint64_t time_index = 0; time_index < wait_time_us / SLEEP_PERIOD_us; time_index++)
487  {
488  if (isServerConnected())
489  {
490  return true;
491  }
492  usleep(SLEEP_PERIOD_us);
493  }
494 
495  return false;
496 }
497 
498 }
499 
500 }
501 
502 #endif //__SHM_ACTION_LIB_H__
共有メモリからトピックを取得する購読者を表現するクラス
Definition: shm_action.hpp:100
共有メモリで受信したリクエストからレスポンスを返すサーバーを表現するクラス
Definition: shm_action.hpp:49
Class that abstracts the method of accessing shared memory.
Definition: shm_base.hpp:86
Basic class definitions for accessing shared memory, ring buffers, etc. The notation is complianted R...