e are going to export ThreadMaster to Python and supply classes for Job and
Condition parameters. Consider the following boost::python declarations.
Lines 3-7. TestJob is a C++ class with void operator()() that supplies idle
stage calculation and occasionally throws an exception.
Lines 8-12. ExitCondition is exactly that. It has bool operator()() that
returns "true" several times and afterwards returns "false".
Lines 13-18. These declarations export boost::function0<void> and
boost::function0<bool> to Python under names "Job" and "Condition".
Line 19. The "makeJob" is a function that transforms a python object into a
C++ boost::function0<void> object.
Lines 20-24. These declare ThreadMaster as a Python class and export its
interface.
1\
void
pythonThreadMaster()
2\
{
3\
class_<TestJob>("TestJob",init<std::string,int,int,int,double>())
4\
.def("__call__",&TestJob::operator())
5\
.def("job",&TestJob::job)
6\
.def("__str__",&TestJob::toString)
7\
;
8\
class_<ExitCondition>("ExitCondition",init<int>())
9\
.def("__call__",&ExitCondition::operator())
10\
.def("condition",&ExitCondition::condition)
11\
.def("__str__",&ExitCondition::toString)
12\
;
13\
class_<boost::function0<void>
>("Job")
14\
.def("__call__",&boost::function0<void>::operator())
15\
;
16\
class_<boost::function0<bool>
>("Condition")
17\
.def("__call__",&boost::function0<bool>::operator())
18\
;
19\
def("makeJob",&makeJob);
20\
class_<ThreadMaster,boost::noncopyable>("ThreadMaster",init<int>())
21\
.def("add",&ThreadMaster::add)
22\
.def("run",&ThreadMaster::run)
23\
.def("isRunning",&ThreadMaster::isRunning)
24\
;
25\
}
The code for TestJob and ExitCondition consist of functional part that we are
about to consider and a standard boost::shared_ptr-based copy-by-value
semantics (also called "Bridge Pattern").
The lines 30-50 contain the functional part of the Job. We put some debugging
information to std::cout in lines 33-36, 41, 47-49. In the line 37-43 we throw
an exception with probability given by the parameter theProb. It eventually
comes from the user via constructor arguments. If the exception is not thrown
then the lines 44-45 do some idle cycling an waiting. Then the code returns.
We supply the parameter "theName" to include it in the std::cout outputs. This
way we can have distinct test stages based on the same code.
The line 77 is included because Python will not convert TestJob into
boost::function0<void> automatically the way C++ would.
1\
class
TestJob
2\
{
3\
private:
4\
class
Impl : boost::noncopyable
5\
{
6\
public:
7\
typedef
ots::config::ExclusiveMutex::type Mutex;
8\
typedef
ots::config::WriterLock<Mutex>::type Lock;
9\
private:
10\
Mutex
theMutex;
11\
std::string
theName;
12\
int
theStartingDelay;
13\
int
thePeriodicDelay;
14\
int
theSteps;
15\
double
theProb;
16\
public:
17\
Impl(
18\
const
std::string& name,
19\
int
startingDelay,
20\
int
periodicDelay,
21\
int
steps,
22\
double
prob
23\
);
30\
void
operator()()
31\
{
32\
ots::threading::Sleep
s(theStartingDelay); //give time to Python to print the next prompt
33\
{
34\
Lock
guard(theMutex);
35\
std::cout<<"Starting
"<<theName<<std::endl;
36\
}
37\
double
u=ots::random::uniform();
38\
if(
0<u && u<theProb )
39\
{
40\
Lock
guard(theMutex);
41\
std::cout<<"Job
"<<theName<<" throwing
boost::thread_resource_error"<<std::endl;
42\
throw
boost::thread_resource_error();
43\
}
44\
for(
int i=0; i<theSteps; ++i )
45\
ots::threading::Sleep
s(thePeriodicDelay);
46\
{
47\
Lock
guard(theMutex);
48\
std::cout<<"Finishing
"<<theName<<std::endl;
49\
}
50\
}
...
61\
};
62\
boost::shared_ptr<Impl>
theImpl;
63\
public:
64\
TestJob(
65\
const
std::string& name,
66\
int
startingDelay,
67\
int
periodicDelay,
68\
int
steps,
69\
double
prob
70\
);
76\
void
operator()() { (*theImpl)(); }
77\
boost::function0<void>
job() const { return *this; }
...
79\
};
The ExitCondition is also Bridge Pattern based but has much simpler functional
code.
1\
class
ExitCondition
2\
{
3\
private:
4\
class
Impl : boost::noncopyable
5\
{
6\
private:
7\
int
theTimes;
8\
public:
9\
explicit
Impl( int times )
10\
:
theTimes(times)
11\
{}
12\
bool
operator()() { return --theTimes>0; }
13\
std::string
toString() const { return
"ExitCondition("+boost::lexical_cast<std::string>(theTimes)+")";
}
14\
};
15\
boost::shared_ptr<Impl>
theImpl;
16\
public:
17\
explicit
ExitCondition( int times )
18\
:
theImpl(new Impl(times))
19\
{}
20\
ExitCondition(
const ExitCondition& e ) : theImpl(e.theImpl) {}
21\
ExitCondition&
operator=( const ExitCondition& e ) { theImpl=e.theImpl; return *this;
}
22\
bool
operator()() { return (*theImpl)(); }
23\
boost::function0<bool>
condition() const { return *this; }
24\
std::string
toString() const { return theImpl->toString(); }
25\
};
The line 12 is the functional code. The "theTimes" parameter is set in the
constructor, lines 17,9. At every call to bool operator()() this parameter is
decreased. While theTimes is positive the ExitCondition returns "true".
Otherwise it returns "false".
We place the following Python code into a file 'test.py':
1\
import
test
2\
tm=test.ThreadMaster(4)
3\
tj1=test.TestJob("job1",100000,10000,100,0.1)
4\
tj2=test.TestJob("job2",100000,10000,100,0.1)
5\
tj3=test.TestJob("job3",100000,10000,100,0.1)
6\
tc=test.ExitCondition(6)
7\
tm.add(tj1.job(),tc.condition())
8\
tm.add(tj2.job(),tc.condition())
9\
tm.add(tj3.job(),tc.condition())
and activate the C++ code with the following Python session. We assume that
the current directory is ThreadMasterTest/ThreadMasterTest_msvc_ide/release of
the ots installation.
In [1]: execfile('../../test.py')
In [2]: tm.run()
In [3]: Starting job1
Starting job1
Starting job1
Starting job1
Finishing job1
Finishing job1
Finishing job1
Finishing job1
Starting job2
Starting job2
Starting job2
Starting job2
Job job2 throwing boost::thread_resource_error
Starting job2
Finishing job2
Finishing job2
Finishing job2
Finishing job2
Starting job3
Starting job3
Starting job3
Starting job3
Finishing job3
Finishing job3
Finishing job3
Finishing job3
Starting job1
Starting job1
Starting job1
Starting job1
Finishing job1
Finishing job1
Finishing job1
Finishing job1
Starting job2
Starting job2
Starting job2
Starting job2
Finishing job2
Finishing job2
Finishing job2
Finishing job2
Starting job3
Starting job3
Starting job3
Starting job3
Job job3 throwing boost::thread_resource_error
Job job3 throwing boost::thread_resource_error
Starting job3
Starting job3
Finishing job3
Finishing job3
Finishing job3
Finishing job3
|