Skip to content

Commit 76a61c1

Browse files
committed
Merge pull request cms-sw#1487 from Dr15Jones/addCallXNoWait
Multithreading fixes -- Added CallOnceNoWait and CallNTimesNoWait
2 parents df5352a + 954a576 commit 76a61c1

File tree

4 files changed

+245
-1
lines changed

4 files changed

+245
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#ifndef FWCore_Utilities_CallNTimesNoWait_h
2+
#define FWCore_Utilities_CallNTimesNoWait_h
3+
// -*- C++ -*-
4+
//
5+
// Package: FWCore/Utilities
6+
// Class : CallNTimesNoWait
7+
//
8+
/**\class edm::CallNTimesNoWait CallNTimesNoWait.h "CallNTimesNoWait.h"
9+
10+
Description: Thread safe way to do something N times
11+
12+
Usage:
13+
This class allows one to safely do a 'non-side-effect' operation N times in a job.
14+
An example use would be
15+
\code
16+
void myFunc( int iValue) {
17+
static CallNTimesNoWait message{2};
18+
message([&]() { edm::LogInfo("IWantToKnow")<<"called with "<<iValue; } );
19+
\endcode
20+
The important thing to remember, is there is no guarantee that the operation being run
21+
finishes before a thread which doesn't get to run the operation reaches the code following
22+
the call. Therefore it is useful to suppress messages but should not be used to do something
23+
like filling a container with values since the filling is not guaranteed to complete before
24+
another thread skips the call.
25+
*/
26+
//
27+
// Original Author: Chris Jones
28+
// Created: Fri, 15 Nov 2013 14:29:41 GMT
29+
//
30+
31+
// system include files
32+
#include<atomic>
33+
34+
// user include files
35+
36+
// forward declarations
37+
namespace edm {
38+
class CallNTimesNoWait
39+
{
40+
41+
public:
42+
CallNTimesNoWait( unsigned short iNTimes ): m_ntimes(static_cast<int>(iNTimes)-1), m_done(false){}
43+
44+
template <typename T>
45+
void operator()(T iCall) {
46+
if(not m_done.load(std::memory_order_acquire) ) {
47+
if(m_ntimes.fetch_sub(1,std::memory_order_acq_rel)<0) {
48+
m_done.store(true,std::memory_order_release);
49+
return;
50+
};
51+
iCall();
52+
}
53+
}
54+
55+
private:
56+
std::atomic<int> m_ntimes;
57+
std::atomic<bool> m_done;
58+
};
59+
}
60+
61+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#ifndef FWCore_Utilities_CallOnceNoWait_h
2+
#define FWCore_Utilities_CallOnceNoWait_h
3+
// -*- C++ -*-
4+
//
5+
// Package: FWCore/Utilities
6+
// Class : CallOnceNoWait
7+
//
8+
/**\class edm::CallOnceNoWait CallOnceNoWait.h "FWCore/Utilities/interface/CallOnceNoWait.h"
9+
10+
Description: Thread safe way to do something 1 time
11+
12+
Usage:
13+
This class allows one to safely do a 'non-side-effect' operation 1 time in a job.
14+
An example use would be
15+
\code
16+
void myFunc( int iValue) {
17+
static CallOnceNoWait message;
18+
message([&]() { edm::LogInfo("IWantToKnow")<<"called with "<<iValue; } );
19+
\endcode
20+
The important thing to remember, is there is no guarantee that the operation being run
21+
finishes before a thread which doesn't get to run the operation reaches the code following
22+
the call. Therefore it is useful to suppress messages but should not be used to do something
23+
like filling a container with values since the filling is not guaranteed to complete before
24+
another thread skips the call.
25+
26+
*/
27+
//
28+
// Original Author: Chris Jones
29+
// Created: Fri, 15 Nov 2013 14:29:51 GMT
30+
//
31+
32+
// system include files
33+
#include <atomic>
34+
35+
// user include files
36+
37+
// forward declarations
38+
39+
namespace edm {
40+
class CallOnceNoWait
41+
{
42+
public:
43+
CallOnceNoWait() : m_called(false) {}
44+
45+
template <typename T>
46+
void operator()(T iCall) {
47+
bool expected = false;
48+
if(m_called.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) {
49+
iCall();
50+
}
51+
}
52+
53+
private:
54+
std::atomic<bool> m_called;
55+
};
56+
}
57+
58+
59+
#endif

FWCore/Utilities/test/BuildFile.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<bin file="MallocOpts_t.cpp">
3636
<use name="cppunit"/>
3737
</bin>
38-
<bin name="testFWCoreUtilities" file="typeidbase_t.cppunit.cpp,typeid_t.cppunit.cpp,cputimer_t.cppunit.cpp,extensioncord_t.cppunit.cpp,friendlyname_t.cppunit.cpp,signal_t.cppunit.cpp,soatuple_t.cppunit.cpp,transform.cppunit.cpp">
38+
<bin name="testFWCoreUtilities" file="typeidbase_t.cppunit.cpp,typeid_t.cppunit.cpp,cputimer_t.cppunit.cpp,extensioncord_t.cppunit.cpp,friendlyname_t.cppunit.cpp,signal_t.cppunit.cpp,soatuple_t.cppunit.cpp,transform.cppunit.cpp,callxnowait_t.cppunit.cpp">
3939
<use name="cppunit"/>
4040
</bin>
4141

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*----------------------------------------------------------------------
2+
3+
Test program for edm::TypeID class.
4+
Changed by Viji on 29-06-2005
5+
6+
----------------------------------------------------------------------*/
7+
8+
#include <cassert>
9+
#include <cppunit/extensions/HelperMacros.h>
10+
#include "FWCore/Utilities/interface/CallOnceNoWait.h"
11+
#include "FWCore/Utilities/interface/CallNTimesNoWait.h"
12+
13+
#include <thread>
14+
15+
class testCallXNoWait: public CppUnit::TestFixture
16+
{
17+
CPPUNIT_TEST_SUITE(testCallXNoWait);
18+
19+
CPPUNIT_TEST(onceTest);
20+
CPPUNIT_TEST(nTimesTest);
21+
CPPUNIT_TEST(onceThreadedTest);
22+
CPPUNIT_TEST(nTimesThreadedTest);
23+
24+
CPPUNIT_TEST_SUITE_END();
25+
public:
26+
void setUp(){}
27+
void tearDown(){}
28+
29+
void onceTest();
30+
void nTimesTest();
31+
32+
void onceThreadedTest();
33+
void nTimesThreadedTest();
34+
};
35+
36+
///registration of the test so that the runner can find it
37+
CPPUNIT_TEST_SUITE_REGISTRATION(testCallXNoWait);
38+
39+
void testCallXNoWait::onceTest()
40+
{
41+
42+
edm::CallOnceNoWait guard;
43+
44+
unsigned int iCount=0;
45+
46+
guard([&iCount](){ ++iCount; });
47+
48+
CPPUNIT_ASSERT(iCount == 1);
49+
50+
guard([&iCount](){ ++iCount; });
51+
CPPUNIT_ASSERT(iCount == 1);
52+
53+
}
54+
55+
void testCallXNoWait::nTimesTest()
56+
{
57+
edm::CallNTimesNoWait guard{3};
58+
59+
unsigned int iCount=0;
60+
61+
for(unsigned int i=0; i<6; ++i) {
62+
guard([&iCount](){ ++iCount; });
63+
if(i<3) {
64+
CPPUNIT_ASSERT(iCount == i+1);
65+
} else {
66+
CPPUNIT_ASSERT(iCount == 3);
67+
}
68+
}
69+
}
70+
71+
72+
void testCallXNoWait::onceThreadedTest()
73+
{
74+
75+
edm::CallOnceNoWait guard;
76+
77+
unsigned int iCount=0;
78+
79+
std::vector<std::thread> threads;
80+
81+
std::atomic<bool> start{false};
82+
83+
for(unsigned int i=0; i<4; ++i) {
84+
threads.emplace_back([&guard,&iCount,&start](){
85+
while(not start) {}
86+
guard([&iCount](){ ++iCount; });
87+
});
88+
}
89+
CPPUNIT_ASSERT(iCount == 0);
90+
91+
start = true;
92+
for(auto& t : threads) {
93+
t.join();
94+
}
95+
CPPUNIT_ASSERT(iCount == 1);
96+
}
97+
98+
99+
void testCallXNoWait::nTimesThreadedTest()
100+
{
101+
const unsigned short kMaxTimes=3;
102+
edm::CallNTimesNoWait guard(kMaxTimes);
103+
104+
unsigned int iCount=0;
105+
106+
std::vector<std::thread> threads;
107+
108+
std::atomic<bool> start{false};
109+
110+
for(unsigned int i=0; i<2*kMaxTimes; ++i) {
111+
threads.emplace_back([&guard,&iCount,&start](){
112+
while(not start) {}
113+
guard([&iCount](){ ++iCount; });
114+
});
115+
}
116+
CPPUNIT_ASSERT(iCount == 0);
117+
118+
start = true;
119+
for(auto& t : threads) {
120+
t.join();
121+
}
122+
CPPUNIT_ASSERT(iCount == kMaxTimes);
123+
}
124+

0 commit comments

Comments
 (0)