API
shmimMonitor.hpp
Go to the documentation of this file.
1 /** \file shmimMonitor.hpp
2  * \brief The MagAO-X generic shared memory monitor.
3  *
4  * \author Jared R. Males (jaredmales@gmail.com)
5  *
6  * \ingroup app_files
7  */
8 
9 #ifndef shmimMonitor_hpp
10 #define shmimMonitor_hpp
11 
12 
13 #include <ImageStreamIO/ImageStruct.h>
14 #include <ImageStreamIO/ImageStreamIO.h>
15 
16 #include "../../libMagAOX/common/paths.hpp"
17 
18 
19 namespace MagAOX
20 {
21 namespace app
22 {
23 namespace dev
24 {
25 
26 struct shmimT
27 {
28  static std::string configSection()
29  {
30  return "shmimMonitor";
31  };
32 
33  static std::string indiPrefix()
34  {
35  return "sm";
36  };
37 };
38 
39 /** MagAO-X generic shared memory monitor
40  *
41  *
42  * The derived class `derivedT` must expose the following interface
43  \code
44 
45  //The allocate function is called after connecting to the shared memory buffer
46  //It should check that the buffer has the expected size, and perform any internal allocations
47  //to prepare for processing.
48  int derivedT::allocate( const specificT & ///< [in] tag to differentiate shmimMonitor parents. Normally this is dev::shmimT for a single parent.
49  );
50 
51  int derivedT::processImage( void * curr_src, ///< [in] pointer to the start of the current frame
52  const specificT & ///< [in] tag to differentiate shmimMonitor parents. Normally this is dev::shmimT for a single parent.
53  )
54  \endcode
55  * Each of the above functions should return 0 on success, and -1 on an error.
56  *
57  * This class should be declared a friend in the derived class, like so:
58  \code
59  friend class dev::shmimMonitor<derivedT, specificT>;
60  \endcode
61  *
62  * Calls to this class's `setupConfig`, `loadConfig`, `appStartup`, `appLogic` and `appShutdown`
63  * functions must be placed in the derived class's functions of the same name.
64  *
65  *
66  * The template specifier `specificT` allows inheritance of multiple shmimMonitor classes. This type must have at least
67  * the static member function:
68  \code
69  static std::string indiPrefix()
70  \endcode
71  * which returns the string to prefix to INDI properties. The default `shmimT` uses "sm".
72  *
73  * \todo move requirement for sigsegv handling to derived class -- it should set m_restart on all shmimMonitors it inherited.
74  *
75  * \ingroup appdev
76  */
77 template<class derivedT, class specificT=shmimT>
79 {
80 protected:
81 
82  /** \name Configurable Parameters
83  * @{
84  */
85  std::string m_shmimName {""}; ///< The name of the shared memory image, is used in `/tmp/<shmimName>.im.shm`. Derived classes should set a default.
86 
87  int m_smThreadPrio {2}; ///< Priority of the shmimMonitor thread, should normally be > 00.
88 
89  std::string m_smCpuset; ///< The cpuset to assign the shmimMonitor thread to. Ignored if empty (the default).
90 
91  ///@}
92 
93  bool m_getExistingFirst {false}; ///< If set to true by derivedT, any existing image will be grabbed and sent to processImage before waiting on the semaphore.
94 
95  int m_semaphoreNumber {5}; ///< The image structure semaphore index.
96 
97  uint32_t m_width {0}; ///< The width of the images in the stream
98  uint32_t m_height {0}; ///< The height of the images in the stream
99  uint32_t m_depth {0}; ///< The depth of the circular buffer in the stream
100 
101  uint8_t m_dataType{0}; ///< The ImageStreamIO type code.
102  size_t m_typeSize {0}; ///< The size of the type, in bytes. Result of sizeof.
103 
104  IMAGE m_imageStream; ///< The ImageStreamIO shared memory buffer.
105 
106  ino_t m_inode {0}; ///< The inode of the image stream file
107 
108 public:
109 
110  /// Setup the configuration system
111  /**
112  * This should be called in `derivedT::setupConfig` as
113  * \code
114  shmimMonitor<derivedT, specificT>::setupConfig(config);
115  \endcode
116  * with appropriate error checking.
117  */
118  void setupConfig(mx::app::appConfigurator & config /**< [out] the derived classes configurator*/);
119 
120  /// load the configuration system results
121  /**
122  * This should be called in `derivedT::loadConfig` as
123  * \code
124  shmimMonitor<derivedT, specificT>::loadConfig(config);
125  \endcode
126  * with appropriate error checking.
127  */
128  void loadConfig(mx::app::appConfigurator & config /**< [in] the derived classes configurator*/);
129 
130  /// Startup function
131  /** Starts the shmimMonitor thread
132  * This should be called in `derivedT::appStartup` as
133  * \code
134  shmimMonitor<derivedT, specificT>::appStartup();
135  \endcode
136  * with appropriate error checking.
137  *
138  * \returns 0 on success
139  * \returns -1 on error, which is logged.
140  */
141  int appStartup();
142 
143  /// Checks the shmimMonitor thread
144  /** This should be called in `derivedT::appLogic` as
145  * \code
146  shmimMonitor<derivedT, specificT>::appLogic();
147  \endcode
148  * with appropriate error checking.
149  *
150  * \returns 0 on success
151  * \returns -1 on error, which is logged.
152  */
153  int appLogic();
154 
155  /// Shuts down the shmimMonitor thread
156  /** This should be called in `derivedT::appShutdown` as
157  * \code
158  shmimMonitor<derivedT, specificT>::appShutdown();
159  \endcode
160  * with appropriate error checking.
161  *
162  * \returns 0 on success
163  * \returns -1 on error, which is logged.
164  */
165  int appShutdown();
166 
167 protected:
168 
169  /** \name SIGSEGV & SIGBUS signal handling
170  * These signals occur as a result of a ImageStreamIO source server resetting (e.g. changing frame sizes).
171  * When they occur a restart of the shmim monitor thread main loops is triggered.
172  *
173  * @{
174  */
175  bool m_restart {false}; ///< Flag indicating tha the shared memory should be reinitialized.
176 
177  static shmimMonitor * m_selfMonitor; ///< Static pointer to this (set in constructor). Used for getting out of the static SIGSEGV handler.
178 
179  ///Sets the handler for SIGSEGV and SIGBUS
180  /** These are caused by ImageStreamIO server resets.
181  */
183 
184  ///The handler called when SIGSEGV or SIGBUS is received, which will be due to ImageStreamIO server resets. Just a wrapper for handlerSigSegv.
185  static void _handlerSigSegv( int signum,
186  siginfo_t *siginf,
187  void *ucont
188  );
189 
190  ///Handles SIGSEGV and SIGBUS. Sets m_restart to true.
191  void handlerSigSegv( int signum,
192  siginfo_t *siginf,
193  void *ucont
194  );
195  ///@}
196 
197  /** \name shmimmonitor Thread
198  * This thread actually monitors the shared memory buffer
199  * @{
200  */
201 
202  bool m_smThreadInit {true}; ///< Synchronizer for thread startup, to allow priority setting to finish.
203 
204  pid_t m_smThreadID {0}; ///< The s.m. thread PID.
205 
206  pcf::IndiProperty m_smThreadProp; ///< The property to hold the s.m. thread details.
207 
208  std::thread m_smThread; ///< A separate thread for the actual monitoring
209 
210  ///Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
211  static void smThreadStart( shmimMonitor * s /**< [in] a pointer to a shmimMonitor instance (normally this) */);
212 
213  /// Execute the monitoring thread
214  void smThreadExec();
215 
216  ///@}
217 
218 
219 
220 
221  /** \name INDI
222  *
223  *@{
224  */
225 protected:
226  //declare our properties
227 
228  pcf::IndiProperty m_indiP_shmimName; ///< Property used to report the shmim buffer name
229 
230  pcf::IndiProperty m_indiP_frameSize; ///< Property used to report the current frame size
231 
232 public:
233 
234  /// Update the INDI properties for this device controller
235  /** You should call this once per main loop.
236  * It is not called automatically.
237  *
238  * \returns 0 on success.
239  * \returns -1 on error.
240  */
241  int updateINDI();
242 
243  ///@}
244 
245 private:
246  derivedT & derived()
247  {
248  return *static_cast<derivedT *>(this);
249  }
250 };
251 
252 //Set self pointer to null so app starts up uninitialized.
253 template<class derivedT, class specificT>
254 shmimMonitor<derivedT, specificT> * shmimMonitor<derivedT, specificT>::m_selfMonitor = nullptr;
255 
256 
257 template<class derivedT, class specificT>
258 void shmimMonitor<derivedT, specificT>::setupConfig(mx::app::appConfigurator & config)
259 {
260  config.add(specificT::configSection()+".threadPrio", "", specificT::configSection()+".threadPrio", argType::Required, specificT::configSection(), "threadPrio", false, "int", "The real-time priority of the shmimMonitor thread.");
261 
262  config.add(specificT::configSection()+".cpuset", "", specificT::configSection()+".cpuset", argType::Required, specificT::configSection(), "cpuset", false, "string", "The cpuset for the shmimMonitor thread.");
263 
264  config.add(specificT::configSection()+".shmimName", "", specificT::configSection()+".shmimName", argType::Required, specificT::configSection(), "shmimName", false, "string", "The name of the ImageStreamIO shared memory image. Will be used as /tmp/<shmimName>.im.shm.");
265 
266  config.add(specificT::configSection()+".getExistingFirst", "", specificT::configSection()+".getExistingFirst", argType::Required, specificT::configSection(), "getExistingFirst", false, "bool", "If true an existing image is loaded. If false we wait for a new image.");
267 
268  //Set this here to allow derived classes to set their own default before calling loadConfig
269  m_shmimName = derived().configName();
270 
271 }
272 
273 template<class derivedT, class specificT>
274 void shmimMonitor<derivedT, specificT>::loadConfig(mx::app::appConfigurator & config)
275 {
276  config(m_smThreadPrio, specificT::configSection() + ".threadPrio");
277  config(m_smCpuset, specificT::configSection() + ".cpuset");
278  config(m_shmimName, specificT::configSection() + ".shmimName");
279  config(m_getExistingFirst, specificT::configSection() + ".getExistingFirst");
280 }
281 
282 template<class derivedT, class specificT>
284 {
285  //Register the shmimName INDI property
286  m_indiP_shmimName = pcf::IndiProperty(pcf::IndiProperty::Text);
287  m_indiP_shmimName.setDevice(derived().configName());
288  m_indiP_shmimName.setName( specificT::indiPrefix() + "_shmimName");
289  m_indiP_shmimName.setPerm(pcf::IndiProperty::ReadOnly);
290  m_indiP_shmimName.setState(pcf::IndiProperty::Idle);
291  m_indiP_shmimName.add(pcf::IndiElement("name"));
292  m_indiP_shmimName["name"] = m_shmimName;
293 
294  if( derived().registerIndiPropertyNew( m_indiP_shmimName, nullptr) < 0)
295  {
296  #ifndef SHMIMMONITOR_TEST_NOLOG
297  derivedT::template log<software_error>({__FILE__,__LINE__});
298  #endif
299  return -1;
300  }
301 
302  //Register the frameSize INDI property
303  m_indiP_frameSize = pcf::IndiProperty(pcf::IndiProperty::Number);
304  m_indiP_frameSize.setDevice(derived().configName());
305  m_indiP_frameSize.setName(specificT::indiPrefix() + "_frameSize");
306  m_indiP_frameSize.setPerm(pcf::IndiProperty::ReadOnly);
307  m_indiP_frameSize.setState(pcf::IndiProperty::Idle);
308  m_indiP_frameSize.add(pcf::IndiElement("width"));
309  m_indiP_frameSize["width"] = 0;
310  m_indiP_frameSize.add(pcf::IndiElement("height"));
311  m_indiP_frameSize["height"] = 0;
312 
313  if(setSigSegvHandler() < 0)
314  {
315  #ifndef SHMIMMONITOR_TEST_NOLOG
316  derivedT::template log<software_error>({__FILE__,__LINE__});
317  #endif
318  return -1;
319  }
320 
321  if( derived().registerIndiPropertyNew( m_indiP_frameSize, nullptr) < 0)
322  {
323  #ifndef SHMIMMONITOR_TEST_NOLOG
324  derivedT::template log<software_error>({__FILE__,__LINE__});
325  #endif
326  return -1;
327  }
328 
329  //Install empty signal handler for USR1, which is used to interrupt sleeps in the monitor threads.
330  struct sigaction act;
331  sigset_t set;
332 
333  act.sa_sigaction = &sigUsr1Handler;
334  act.sa_flags = SA_SIGINFO;
335  sigemptyset(&set);
336  act.sa_mask = set;
337 
338  errno = 0;
339  if( sigaction(SIGUSR1, &act, 0) < 0 )
340  {
341  std::string logss = "Setting handler for SIGUSR1 failed. Errno says: ";
342  logss += strerror(errno);
343 
344  derivedT::template log<software_error>({__FILE__, __LINE__, errno, 0, logss});
345 
346  return -1;
347  }
348 
349  if(derived().threadStart( m_smThread, m_smThreadInit, m_smThreadID, m_smThreadProp, m_smThreadPrio, m_smCpuset, specificT::configSection(), this, smThreadStart) < 0)
350  {
351  derivedT::template log<software_error>({__FILE__, __LINE__});
352  return -1;
353  }
354 
355  return 0;
356 
357 }
358 
359 template<class derivedT, class specificT>
361 {
362  //do a join check to see if other threads have exited.
363  if(pthread_tryjoin_np(m_smThread.native_handle(),0) == 0)
364  {
365  derivedT::template log<software_error>({__FILE__, __LINE__, "shmimMonitor thread " + std::to_string(m_smThreadID) + " has exited"});
366 
367  return -1;
368  }
369 
370  return 0;
371 
372 }
373 
374 template<class derivedT, class specificT>
376 {
377  if(m_smThread.joinable())
378  {
379  pthread_kill(m_smThread.native_handle(), SIGUSR1);
380  try
381  {
382  m_smThread.join(); //this will throw if it was already joined
383  }
384  catch(...)
385  {
386  }
387  }
388 
389  return 0;
390 }
391 
392 template<class derivedT, class specificT>
394 {
395  struct sigaction act;
396  sigset_t set;
397 
399  act.sa_flags = SA_SIGINFO;
400  sigemptyset(&set);
401  act.sa_mask = set;
402 
403  errno = 0;
404  if( sigaction(SIGSEGV, &act, 0) < 0 )
405  {
406  std::string logss = "Setting handler for SIGSEGV failed. Errno says: ";
407  logss += strerror(errno);
408 
409  derivedT::template log<software_error>({__FILE__, __LINE__, errno, 0, logss});
410 
411  return -1;
412  }
413 
414  errno = 0;
415  if( sigaction(SIGBUS, &act, 0) < 0 )
416  {
417  std::string logss = "Setting handler for SIGBUS failed. Errno says: ";
418  logss += strerror(errno);
419 
420  derivedT::template log<software_error>({__FILE__, __LINE__, errno, 0,logss});
421 
422  return -1;
423  }
424 
425  derivedT::template log<text_log>("Installed SIGSEGV/SIGBUS signal handler.", logPrio::LOG_DEBUG);
426 
427  return 0;
428 }
429 
430 template<class derivedT, class specificT>
432  siginfo_t *siginf,
433  void *ucont
434  )
435 {
436  m_selfMonitor->handlerSigSegv(signum, siginf, ucont);
437 }
438 
439 template<class derivedT, class specificT>
441  siginfo_t *siginf,
442  void *ucont
443  )
444 {
445  static_cast<void>(signum);
446  static_cast<void>(siginf);
447  static_cast<void>(ucont);
448 
449  m_restart = true;
450 
451  return;
452 }
453 
454 template<class derivedT, class specificT>
456 {
457  s->smThreadExec();
458 }
459 
460 
461 template<class derivedT, class specificT>
463 {
464  m_smThreadID = syscall(SYS_gettid);
465 
466  //Wait for the thread starter to finish initializing this thread.
467  while( m_smThreadInit == true && derived().shutdown() == 0)
468  {
469  sleep(1);
470  }
471 
472  bool opened = false;
473 
474  //bool semgot = false;
475 
476  while(derived().shutdown() == 0)
477  {
478  while((derived().state() != stateCodes::OPERATING || m_shmimName == "" ) && !derived().shutdown() && !m_restart )
479  {
480  sleep(1);
481  }
482 
483  if(derived().shutdown()) return;
484 
485  /* Initialize ImageStreamIO
486  */
487  opened = false;
488  m_restart = false; //Set this up front, since we're about to restart.
489 
490  int logged = 0;
491  while(!opened && !derived().m_shutdown && !m_restart && derived().state() == stateCodes::OPERATING)
492  {
493  //b/c ImageStreamIO prints every single time, and latest version don't support stopping it yet, and that isn't thread-safe-able anyway
494  //we do our own checks. This is the same code in ImageStreamIO_openIm...
495  int SM_fd;
496  char SM_fname[200];
497  ImageStreamIO_filename(SM_fname, sizeof(SM_fname), m_shmimName.c_str());
498  SM_fd = open(SM_fname, O_RDWR);
499  if(SM_fd == -1)
500  {
501  if(!logged) derivedT::template log<text_log>("ImageStream " + m_shmimName + " not found (yet). Retrying . . .", logPrio::LOG_NOTICE);
502  logged = 1;
503  sleep(1); //be patient
504  continue;
505  }
506 
507  //Found and opened, close it and then use ImageStreamIO
508  logged = 0;
509  close(SM_fd);
510 
511  if( ImageStreamIO_openIm(&m_imageStream, m_shmimName.c_str()) == 0)
512  {
513  if(m_imageStream.md[0].sem <= m_semaphoreNumber) ///<\todo this isn't right--> isn't there a define in cacao to use?
514  {
515  ImageStreamIO_closeIm(&m_imageStream);
516  mx::sys::sleep(1); //We just need to wait for the server process to finish startup.
517  }
518  else
519  {
520  opened = true;
521  char SM_fname[200];
522  ImageStreamIO_filename(SM_fname, sizeof(SM_fname), m_shmimName.c_str());
523 
524  struct stat buffer;
525  int rv = stat(SM_fname, &buffer);
526 
527  if(rv != 0)
528  {
529  derivedT::template log<software_critical>({__FILE__,__LINE__, errno, "Could not get inode for " + m_shmimName + ". Source process will need to be restarted."});
530  ImageStreamIO_closeIm(&m_imageStream);
531  return;
532  }
533  m_inode = buffer.st_ino;
534  }
535  }
536  else
537  {
538  mx::sys::sleep(1); //be patient
539  }
540  }
541 
542 
543 
544  if(m_restart) continue; //this is kinda dumb. we just go around on restart, so why test in the while loop at all?
545 
546  if(derived().state() != stateCodes::OPERATING) continue;
547 
548  if(derived().m_shutdown)
549  {
550  if(!opened) return;
551 
552  ImageStreamIO_closeIm(&m_imageStream);
553  return;
554  }
555 
556  m_semaphoreNumber = ImageStreamIO_getsemwaitindex(&m_imageStream, m_semaphoreNumber); //ask for semaphore we had before
557 
558  if(m_semaphoreNumber < 0)
559  {
560  derivedT::template log<software_critical>({__FILE__,__LINE__, "No valid semaphore found for " + m_shmimName + ". Source process will need to be restarted."});
561  return;
562  }
563 
564  derivedT::template log<software_info>({__FILE__,__LINE__, "got semaphore index " + std::to_string(m_semaphoreNumber) + " for " + m_shmimName });
565 
566  ImageStreamIO_semflush(&m_imageStream, m_semaphoreNumber);
567 
568  sem_t * sem = m_imageStream.semptr[m_semaphoreNumber]; ///< The semaphore to monitor for new image data
569 
570  m_dataType = m_imageStream.md[0].datatype;
571  m_typeSize = ImageStreamIO_typesize(m_dataType);
572  m_width = m_imageStream.md[0].size[0];
573  int dim = 1;
574  if(m_imageStream.md[0].naxis > 1)
575  {
576  m_height = m_imageStream.md[0].size[1];
577 
578  if(m_imageStream.md[0].naxis > 2)
579  {
580  dim = 3;
581  m_depth = m_imageStream.md[0].size[2];
582  }
583  else
584  {
585  dim = 2;
586  m_depth = 1;
587  }
588  }
589  else
590  {
591  m_height = 1;
592  m_depth = 1;
593  }
594 
595  if( derived().allocate( specificT()) < 0)
596  {
597  derivedT::template log<software_error>({__FILE__,__LINE__, "allocation failed"});
598  break;
599  }
600 
601  uint8_t atype;
602  size_t snx, sny, snz;
603  uint64_t curr_image; //The current cnt1 index
604 
605  if(m_getExistingFirst && !m_restart && derived().shutdown() == 0) //If true, we always get the existing image without waiting on the semaphore.
606  {
607  if(m_imageStream.md[0].size[2] > 0) ///\todo change to naxis?
608  {
609  curr_image = m_imageStream.md[0].cnt1;
610  }
611  else curr_image = 0;
612 
613  atype = m_imageStream.md[0].datatype;
614  snx = m_imageStream.md[0].size[0];
615 
616  if(dim == 2)
617  {
618  sny = m_imageStream.md[0].size[1];
619  snz = 1;
620  }
621  else if(dim == 3)
622  {
623  sny = m_imageStream.md[0].size[1];
624  snz = m_imageStream.md[0].size[2];
625  }
626  else
627  {
628  sny = 1;
629  snz = 1;
630  }
631 
632  if( atype!= m_dataType || snx != m_width || sny != m_height || snz != m_depth )
633  {
634  continue; //exit the nearest while loop and get the new image setup.
635  }
636 
637  char * curr_src = (char *) m_imageStream.array.raw + curr_image*m_width*m_height*m_typeSize;
638 
639  if( derived().processImage(curr_src, specificT()) < 0)
640  {
641  derivedT::template log<software_error>({__FILE__,__LINE__});
642  }
643  }
644 
645  //This is the main image grabbing loop.
646  while( derived().shutdown() == 0 && !m_restart && derived().state() == stateCodes::OPERATING)
647  {
648 
649  timespec ts;
650 
651  if(clock_gettime(CLOCK_REALTIME, &ts) < 0)
652  {
653  derivedT::template log<software_critical>({__FILE__,__LINE__,errno,0,"clock_gettime"});
654  return;
655  }
656 
657  ts.tv_sec += 1;
658 
659  if(sem_timedwait(sem, &ts) == 0)
660  {
661  if(m_imageStream.md[0].size[2] > 0) ///\todo change to naxis?
662  {
663  curr_image = m_imageStream.md[0].cnt1;
664  }
665  else curr_image = 0;
666 
667  atype = m_imageStream.md[0].datatype;
668  snx = m_imageStream.md[0].size[0];
669 
670  if(dim == 2)
671  {
672  sny = m_imageStream.md[0].size[1];
673  snz = 1;
674  }
675  else if(dim == 3)
676  {
677  sny = m_imageStream.md[0].size[1];
678  snz = m_imageStream.md[0].size[2];
679  }
680  else
681  {
682  sny = 1;
683  snz = 1;
684  }
685 
686  if( atype!= m_dataType || snx != m_width || sny != m_height || snz != m_depth )
687  {
688  break; //exit the nearest while loop and get the new image setup.
689  }
690 
691  if(derived().shutdown() != 0 || m_restart || derived().state() != stateCodes::OPERATING) break; //Check for exit signals
692 
693  char * curr_src = (char *) m_imageStream.array.raw + curr_image*m_width*m_height*m_typeSize;
694 
695  if( derived().processImage(curr_src, specificT()) < 0)
696  {
697  derivedT::template log<software_error>({__FILE__,__LINE__});
698  }
699  }
700  else
701  {
702  if(m_imageStream.md[0].sem <= 0) break; //Indicates that the server has cleaned up.
703 
704  //Check for why we timed out
705  if(errno == EINTR) break; //This indicates signal interrupted us, time to restart or shutdown, loop will exit normally if flags set.
706 
707  //ETIMEDOUT means we should check for deletion, and then wait more.
708  //Otherwise, report an error.
709  if(errno != ETIMEDOUT)
710  {
711  derivedT::template log<software_error>({__FILE__, __LINE__,errno, "sem_timedwait"});
712  break;
713  }
714 
715  //Check if the file has disappeared.
716  int SM_fd;
717  char SM_fname[200];
718  ImageStreamIO_filename(SM_fname, sizeof(SM_fname), m_shmimName.c_str());
719  SM_fd = open(SM_fname, O_RDWR);
720  if(SM_fd == -1)
721  {
722  m_restart = true;
723  }
724  close(SM_fd);
725 
726  //Check if the inode changed
727  struct stat buffer;
728  int rv = stat(SM_fname, &buffer);
729  if(rv != 0)
730  {
731  m_restart = true;
732  }
733 
734  if(buffer.st_ino != m_inode)
735  {
736  m_restart = true;
737  }
738 
739  }
740 
741  }
742 
743  //*******
744  // call derived().cleanup()
745  //*******
746 
747  //opened == true if we can get to this
748  if(m_semaphoreNumber >= 0) m_imageStream.semReadPID[m_semaphoreNumber] = 0; //release semaphore
749  ImageStreamIO_closeIm(&m_imageStream);
750  opened = false;
751 
752  } //outer loop, will exit if m_shutdown==true
753 
754  //derivedT::template log<software_error>({__FILE__,__LINE__, std::to_string(m_smThreadID) + " " + std::to_string(derived().shutdown() == 0)});
755 
756  //*******
757  // call derived().cleanup()
758  //*******
759 
760  if(opened)
761  {
762  ImageStreamIO_closeIm(&m_imageStream);
763  opened = false;
764  }
765 
766 }
767 
768 template<class derivedT, class specificT>
770 {
771  if( !derived().m_indiDriver ) return 0;
772 
773  indi::updateIfChanged(m_indiP_shmimName, "name", m_shmimName, derived().m_indiDriver);
774  indi::updateIfChanged(m_indiP_frameSize, "width", m_width, derived().m_indiDriver);
775  indi::updateIfChanged(m_indiP_frameSize, "height", m_height, derived().m_indiDriver);
776 
777 
778  return 0;
779 }
780 
781 
782 } //namespace dev
783 } //namespace app
784 } //namespace MagAOX
785 #endif
bool m_smThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
uint32_t m_depth
The depth of the circular buffer in the stream.
int appStartup()
Startup function.
pid_t m_smThreadID
The s.m. thread PID.
void handlerSigSegv(int signum, siginfo_t *siginf, void *ucont)
Handles SIGSEGV and SIGBUS. Sets m_restart to true.
void smThreadExec()
Execute the monitoring thread.
static shmimMonitor * m_selfMonitor
Static pointer to this (set in constructor). Used for getting out of the static SIGSEGV handler.
uint32_t m_width
The width of the images in the stream.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
std::thread m_smThread
A separate thread for the actual monitoring.
int m_smThreadPrio
Priority of the shmimMonitor thread, should normally be > 00.
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
pcf::IndiProperty m_indiP_shmimName
Property used to report the shmim buffer name.
pcf::IndiProperty m_indiP_frameSize
Property used to report the current frame size.
static void smThreadStart(shmimMonitor *s)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
int m_semaphoreNumber
The image structure semaphore index.
std::string m_shmimName
The name of the shared memory image, is used in /tmp/<shmimName>.im.shm. Derived classes should set a...
int appShutdown()
Shuts down the shmimMonitor thread.
uint8_t m_dataType
The ImageStreamIO type code.
bool m_restart
Flag indicating tha the shared memory should be reinitialized.
pcf::IndiProperty m_smThreadProp
The property to hold the s.m. thread details.
static void _handlerSigSegv(int signum, siginfo_t *siginf, void *ucont)
The handler called when SIGSEGV or SIGBUS is received, which will be due to ImageStreamIO server rese...
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
ino_t m_inode
The inode of the image stream file.
void setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
std::string m_smCpuset
The cpuset to assign the shmimMonitor thread to. Ignored if empty (the default).
int setSigSegvHandler()
Sets the handler for SIGSEGV and SIGBUS.
bool m_getExistingFirst
If set to true by derivedT, any existing image will be grabbed and sent to processImage before waitin...
void loadConfig(mx::app::appConfigurator &config)
load the configuration system results
@ OPERATING
The device is operating, other than homing.
Definition: stateCodes.hpp:50
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
Definition: indiUtils.hpp:95
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
Definition: MagAOXApp.cpp:42
Definition: dm.hpp:24
constexpr static logPrioT LOG_DEBUG
Used for debugging.
Definition: logPriority.hpp:52
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
Definition: logPriority.hpp:46
static std::string configSection()
static std::string indiPrefix()