Version: 9.15.0
AlternateThreadPT.cxx
Go to the documentation of this file.
1 // Copyright (C) 2006-2025 CEA, EDF
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 
20 #include <iostream>
21 #include <typeinfo>
22 
23 #include "AlternateThreadPT.hxx"
24 
25 //#define _DEVDEBUG_
26 #include "YacsTrace.hxx"
27 
28 using namespace std;
29 using namespace YACS::BASES;
30 
31 AlternateThreadPT::AlternateThreadPT()
32  : _threadStatus(UNEXISTING)
33 {
34  YASSERT(pthread_cond_init(&_pingPongCond, NULL) == 0)
35  YASSERT(pthread_mutex_init(&_pingPongMutex, NULL) == 0)
36 }
37 
39 {
40  try {
42  YASSERT(pthread_mutex_destroy(&_pingPongMutex) == 0)
43  YASSERT(pthread_cond_destroy(&_pingPongCond) == 0)
44  } catch (const exception & e) {
45  cerr << "Exception happened in AlternateThreadPT destructor: " << e.what() << endl;
46  } catch (...) {
47  cerr << "Unknown exception happened in AlternateThreadPT destructor." << endl;
48  }
49 }
50 
52 {
53  // This method must not be called if a slave thread is running
55 
56  YASSERT(pthread_mutex_lock(&_pingPongMutex) == 0)
57  DEBTRACE("Starting thread")
58  YASSERT(pthread_create(&_threadId, 0, runThread, this) == 0)
59  DEBTRACE("Master waiting for slave")
60  YASSERT(pthread_cond_wait(&_pingPongCond, &_pingPongMutex) == 0)
61  DEBTRACE("Master running again")
62 }
63 
65 {
66  // This method must not be called by the slave thread
67  YASSERT(_threadStatus == UNEXISTING || !pthread_equal(pthread_self(), _threadId))
68 
69  switch (_threadStatus) {
70  case UNEXISTING:
71  return;
72  case READY_TO_JOIN:
73  break;
74  case NORMAL_CYCLE:
75  {
76  // First try to signal the slave thread to end properly
77  DEBTRACE("Master is trying to terminate slave by signaling error")
80 
82  // Try to cancel the thread
83  cerr << "Warning: Slave thread in AlternateThread did not end properly. "
84  "Thread will be canceled." << endl;
85  YASSERT(pthread_cancel(_threadId) == 0)
86  YASSERT(pthread_cond_wait(&_pingPongCond, &_pingPongMutex) == 0)
87 
89  // If cancel failed, we can do nothing more, throw an exception
90  YASSERT(false);
91  }
92  }
93  break;
94  }
95  default:
96  YASSERT(false)
97  }
98 
99  // Finally join the thread
100  YASSERT(pthread_mutex_unlock(&_pingPongMutex) == 0)
101  YASSERT(pthread_join(_threadId, NULL) == 0)
103  DEBTRACE("AlternateThread terminated")
104 }
105 
107 {
108  YASSERT(!pthread_equal(pthread_self(), _threadId))
109  DEBTRACE("Master signaling slave and waiting");
110  signalAndWait();
111  DEBTRACE("Master running again");
112 }
113 
115 {
116  YASSERT(pthread_equal(pthread_self(), _threadId))
117  DEBTRACE("Slave signaling master and waiting");
118  signalAndWait();
119  DEBTRACE("Slave running again");
120 }
121 
123 {
125  YASSERT(pthread_cond_signal(&_pingPongCond) == 0)
126  YASSERT(pthread_cond_wait(&_pingPongCond, &_pingPongMutex) == 0)
127 }
128 
130 {
132 }
133 
135 {
136  return _threadStatus;
137 }
138 
139 void * AlternateThreadPT::runThread(void * instance)
140 {
141  try {
142  AlternateThreadPT * instanceCst = (AlternateThreadPT *)instance;
143  YASSERT(pthread_mutex_lock(&instanceCst->_pingPongMutex) == 0)
144  DEBTRACE("Slave thread is now running")
145  instanceCst->_threadStatus = NORMAL_CYCLE;
146  pthread_cleanup_push(threadCleanupFct, instance);
147  instanceCst->run();
148  pthread_cleanup_pop(1);
149  } catch (const exception & e) {
150  cerr << "Unrecoverable error: an exception was caught in AlternateThread "
151  "(exceptions should normally be caught before getting here). "
152  "Exception type is: " << typeid(e).name() << ", message is: " <<
153  e.what() << endl;
154  threadCleanupFct(instance);
155  }
156  // We can't catch (...) here because it causes problems with thread cancellation, at least
157  // with gcc 4.1.2 and older. With newer versions it should be possible to do something like
158  // catch (abi::__forced_unwind e) { throw; }. See
159  // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=28145 for details.
160  // The problem is that no exception should get out of the thread scope without being caught
161  // (otherwise the program aborts). So for now the user has to take care of catching all
162  // exceptions, but he cannot use catch (...).
163  return NULL;
164 }
165 
167 {
168  // Beware of not throwing exceptions in this method
169  DEBTRACE("Cleaning up slave thread")
170  AlternateThreadPT * instanceCst = (AlternateThreadPT *)instance;
171  instanceCst->_threadStatus = READY_TO_JOIN;
172  pthread_cond_signal(&instanceCst->_pingPongCond);
173  pthread_mutex_unlock(&instanceCst->_pingPongMutex);
174 }
#define YASSERT(val)
YASSERT macro is always defined, used like assert, but throw a YACS::Exception instead of abort.
Definition: YacsTrace.hxx:59
#define DEBTRACE(msg)
Definition: YacsTrace.hxx:31
This class provides a mechanism to run two threads alternately.
virtual void signalSlaveAndWait()
Block the master thread and release the slave thread.
bool isTerminationRequested() const
Return true if the master requested the slave thread termination.
virtual void run()=0
This method must be implemented in subclasses and will be run in the slave thread.
virtual void signalMasterAndWait()
Block the slave thread and release the master thread.
ThreadStatus getThreadStatus() const
Return the thread status.
void terminateSlaveThread()
Terminate the slave thread.
static void threadCleanupFct(void *instance)
void start()
Create and launch the slave thread.
static void * runThread(void *instance)