I find that Visual Sudio 2012 makes std::mutex copy constructor private, so I think it can only be passed by reference or pointer, and I test both of them, but to my surprise, the pointer style pass, while the reference style rejected by the compiler, and it says:
" error C2248: 'std::mutex::mutex' : cannot access private member declared in class 'std::mutex' ", so the compiler assumes that I try to copy the std::mutex, but in fact I pass it by reference! Anyone have a experience about that? I list my code here:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
struct SharedMemory
{
public:
int s;
std::mutex mutex;
public:
SharedMemory( int s = 1 ) : s(s){}
void write( int s )
{
mutex.lock();
this->s = s;
mutex.unlock();
}
int read()
{
int tmp;
mutex.lock();
tmp = this->s;
mutex.unlock();
return tmp;
}
void print()
{
std::cout << read() << std::endl;
}
};
void modify( SharedMemory& sm, int i ) // **must use the pointer to the shared memory**
{
//sm->write(i);
sm.write(i);
}
int main( int argc, char* argv[] )
{
SharedMemory sm;
SharedMemory& tmp = sm;
std::vector< std::thread > vec;
for( int i = 0; i < 10; ++i )
{
vec.push_back( std::thread( modify, sm, i ) );
sm.print();
}
for( auto& it : vec ) it.join();
return 0;
}
Related
I'm rephrasing this question more simply, and with a simpler MCVE after a prior version didn't gain much traction.
I had formed the impression that after main() ends, all that happens is global object destruction, then static object destruction, in that order.
I'd never considered the possibility of implications of other "stuff" happening during this period between end of main() and end of the process. But I've recently been working with Linux timers, and experimentally, it appears that timers' callbacks can be invoked during this "late phase" of a process, after main() exits and even after static global objects have been destroyed.
Question: Is that assessment correct? Can a timer callback be invoked after static global objects have been destroyed?
I'd never given much thought to what happens this "late" in a process' lifetime. I suppose I'd naively assumed "something" "prevented" "stuff happening" after main() exited.
Question: My timer callback uses a static global object -- the intent being that the object would "always" be around, regardless of when the callback was invoked. But if timer callbacks can be invoked after static global objects have been destroyed, then that strategy isn't safe. Is there a well-known/correct way to handle this: i.e. prevent timer callbacks from ever accessing invalid objects/memory?
The code below creates "many" timers set to expire 2 seconds in the future, whose callback references a static global object. main() exits right around the middle of when timer callbacks are being invoked. couts show that the static global object is destroyed while timer callbacks are still being invoked.
// main.cpp
#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstring>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include <unistd.h>
using namespace std;
static int tmp = ((srand ( time( NULL ) )), 0);
class Foo { // Encapsulates a random-sized, random-content string.
public:
Foo() {
uint32_t size = (rand() % 24) + 1;
std::generate_n( std::back_inserter( s_ ), size, randChar );
}
void operator=( const Foo& other ) { s_ = other.s_; }
std::string s_;
private:
static char randChar() { return ('a' + rand() % 26); }
};
class GlobalObj { // Encapsulates a map<timer_t, Foo>.
public:
~GlobalObj() { std::cout << __FUNCTION__ << std::endl; }
Foo* getFoo( const timer_t& timer ) {
Foo* ret = NULL;
{
std::lock_guard<std::mutex> l( mutex_ );
std::map<timer_t, Foo*>::iterator i = map_.find( timer );
if ( map_.end() != i ) {
ret = i->second;
map_.erase( i );
}
}
return ret;
}
void setFoo( const timer_t& timer, Foo* foo ) {
std::lock_guard<std::mutex> l( mutex_ );
map_[timer] = foo;
}
private:
std::mutex mutex_;
std::map<timer_t, Foo*> map_;
};
static GlobalObj global_obj; // static global GlobalObj instance.
void osTimerCallback( union sigval sv ) { // The timer callback
timer_t* timer = (timer_t*)(sv.sival_ptr);
if ( timer ) {
Foo* foo = global_obj.getFoo(*timer);
if ( foo ) {
cout << "timer[" << *timer << "]: " << foo->s_ << endl;
delete foo;
}
delete timer;
}
}
bool createTimer( const struct timespec& when ) { // Creates an armed timer.
timer_t* timer = new timer_t;
struct sigevent se;
static clockid_t clock_id =
#ifdef CLOCK_MONOTONIC
CLOCK_MONOTONIC;
#else
CLOCK_REALTIME;
#endif
memset( &se, 0, sizeof se );
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_ptr = timer;
se.sigev_notify_function = osTimerCallback;
if ( timer_create( clock_id, &se, timer ) ) {
cerr << "timer_create() err " << errno << " " << strerror( errno ) << endl;
return false;
}
{
struct itimerspec its;
memset( &its, 0, sizeof its );
its.it_value.tv_sec = when.tv_sec;
its.it_value.tv_nsec = when.tv_nsec;
if ( timer_settime( *timer, 0, &its, NULL ) ) {
cerr << "timer_settime err " << errno << " " << strerror( errno ) << endl;
return false;
}
global_obj.setFoo( *timer, new Foo );
}
return true;
}
int main( int argc, char* argv[] ) { // Creates many armed timers, then exits
static const struct timespec when = { 2, 0 };
for ( uint32_t i = 0; i < 100; ++i ) {
createTimer( when );
}
usleep( 2000010 );
return 0;
}
Example error:
$ g++ --version && g++ -g ./main.cpp -lrt && ./a.out
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
timer[timer[~GlobalObj0x55b34c17bd700x55b34c17be60
]: gx
*** Error in `./a.out': double free or corruption (fasttop): 0xtimer[0x55b34c17bf50]: wsngolhdjvhx
]: npscgelwujjfp
Aborted
Note that the error mentions "double free"; the code above has two delete statements: removing them does not seem to impact reproducibility of the problem. I believe the error message is a red herring, due to accessing invalidated memory.
Increasing the usleep() in main() to sufficiently large so as to allow all timer callback invocations to occur before the static global object destruction results in consistently successful execution.
No, there is no magic preventing a timer to fire after the end of main.
The common way to prevent things like this happening in C++ is to create a small resource owning class for each type of resource that needs to be manually released. See RAII and The rule of three/five/zero.
The foundation for such a class could look like this:
#include <cerrno> // errno
#include <cstring> // std::strerror
#include <stdexcept> // std::runtime_error
#include <string> // std::string
#include <utility> // std::exchange
class Timer {
public:
Timer(clockid_t clockid, sigevent& sev) {
if(timer_create(clockid, &sev, &timerid))
throw std::runtime_error(std::string("timer_create: ") +
std::strerror(errno));
}
// rule of 5
Timer(const Timer&) = delete; // no copy construction
Timer(Timer&& rhs) : // move construction ok
timerid(std::exchange(rhs.timerid, nullptr)) {}
Timer& operator=(const Timer&) = delete; // no copy assignment
Timer& operator=(Timer&& rhs) { // move assignment ok
if(this != &rhs) {
if(timerid) timer_delete(timerid);
timerid = std::exchange(rhs.timerid, nullptr);
}
return *this;
}
~Timer() {
if(timerid) timer_delete(timerid);
}
private:
timer_t timerid;
};
You can now store your Timers in a container and they will be properly deleted when the container goes out of scope.
Having this approach whenever one has to deal with one of these create / delete pairs, that are commonly found in C API:s, usually limits the number of surprises like the one you got.
Also read about the Static Initialization Order Fiasco to avoid other potential pitfalls.
Note: This implementation makes use of the fact that timer_t is a pointer type on my system and I don't know if that's always the case.
What is the drawbacks or errors in the following approach to use non-owning std::unique_ptr having custom deleter to arrange a critical section?
#include <memory>
#include <shared_mutex>
#include <optional>
#include <variant>
#include <cassert>
struct Data
{
std::optional<int> i;
};
struct DataLocker
{
std::variant<std::unique_lock<std::shared_mutex>, std::shared_lock<std::shared_mutex>> lock;
void operator () (const Data *)
{
std::visit([] (auto & lock) { if (lock) lock.unlock(); }, lock);
}
};
struct DataHolder
{
std::unique_ptr<Data, DataLocker> getLockedData()
{
return {&data, {std::unique_lock<std::shared_mutex>{m}}};
}
std::unique_ptr<const Data, DataLocker> getLockedData() const
{
return {&data, {std::shared_lock<std::shared_mutex>{m}}};
}
private :
mutable std::shared_mutex m;
Data data;
};
#include <iostream>
#include <thread>
int main()
{
DataHolder d;
auto producer = [&d]
{
d.getLockedData()->i = 123;
};
auto consumer = [&d = std::as_const(d)]
{
for (;;) {
if (const auto i = d.getLockedData()->i) {
std::cout << *i << std::endl;
return;
}
}
};
std::thread p(producer);
std::thread c(consumer);
p.join();
c.join();
}
One corner case, when writer reset()s a pointer and never destruct std::unique_ptr itself is covered by adding unlock to deleter's operator ().
I cannot move a std::vector<std::unique_ptr<..>> from a function:
MSVC complains (C2280) about attempting to reference a deleted function.
How would this work?
#include <vector>
#include <iostream>
#include <memory>
using namespace std;
class foo {
public:
int i;
};
vector<unique_ptr<foo>> test() {
vector<unique_ptr<foo>> ret{};
auto f = make_unique<foo>();
f->i = 1;
ret.push_back(move(f));
return move(ret);
}
int main(int argc, char** argv) {
auto t = test();
for (auto j : t) {
// fails here: --^
cout << j->i << endl;
}
getchar();
}
The complete error message reads:
'std::unique_ptr>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
It's not the function, it's the loop...
for (auto j : t)
... which attempts to copy-initialize j for each element of t in turn. Recall that a plain auto means value semantics. Use a reference instead:
for (auto const& j : t)
Here is a mini-example of my code, how to correctly initialize member pool_ in the constructor.
#include <vector>
#include <thread>
#include <iostream>
namespace b {
class A;
typedef void (A::*Func) (void);
struct c {
Func fun;
int num;
std::vector<std::thread> threads;
};
class A {
public:
A() {
pool_ = {{&A::func1, 1, }, {&A::func2, 2, }}; // how to initialize?
}
private:
std::vector<c> pool_;
void func1(void) { std::cout << "func1\n"; };
void func2(void) { std::cout << "func2\n"; };
void CreateThread(c& pool) {
for (int i = 0; i < pool.num; ++i) {
pool.threads.push_back(std::thread(pool.fun, this));
}
}
};
} // namespace b
int main() {
b::A a;
return 0;
}
Platform: Ubuntu 14.04 with g++ 4.8.4
compile command:
g++ -Wall -std=c++11 test.cc -lpthread -o test
The major error message is:
error: use of deleted function ‘std::thread::thread(std::thread&)’
I know it is because copy construction and assignment of std::thread is not allowed. But I tried other ways and failed.
Two steps to solving this elegantly:
provide a constructor for c that does 'the right thing'
struct c {
c(Func fun, int num, std::vector<std::thread> threads = {})
: fun(fun)
, num(num)
, threads(std::move(threads))
{}
Func fun;
int num;
std::vector<std::thread> threads;
};
Then neatly emplace your objects into pool_
A()
{
pool_.emplace_back(&A::func1, 1);
pool_.emplace_back(&A::func2, 2);
}
I have a counter that is being incremented from one thread. In the main thread, I basically print it out by calling data member of a class. In the below code, nothing is being printed out.
#include <iostream>
#include <thread>
#include <windows.h>
#include <mutex>
std::mutex mut;
class Foo
{
public:
Foo(const int& m) : m_delay(m), m_count(0)
{}
void update()
{
std::cout << "count: " << this->m_count << std::endl;
}
void operator()()
{
while (true){
mut.lock();
m_count++;
mut.unlock();
Sleep(m_delay);
}
}
private:
int m_delay;
int m_count;
};
Foo *obj = new Foo(200);
int main()
{
std::thread *t = new std::thread(*obj);
t->join();
while(true)
{
obj->update();
Sleep(10);
}
return 0;
}
The problem with the original code is that this copies the Foo object:
std::thread *t = new std::thread(*obj);
That means that the increments happen to the copy, and so the value in the original Foo never changes, and so when main prints it out (if you move the misplaced join()) the value is always the same.
A solution is to use a reference not a copy:
std::thread *t = new std::thread(std::ref(*obj));
You also need to protect the read of the variable by the mutex (or use std::atomic<int> for the counter) to avoid undefined behaviour caused by concurrently reading and writing a non-atomic variable.
You should also stop using mut.lock() and mut.unlock() directly, use a scoped lock instead.
There's also no need to create things on the heap unnecessarily, overusing new is a bad habit of people who learnt Java and C# first.
You can also make the code portable by replacing the Windows-specific Sleep call with standard C++.
A correct version would be:
#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>
std::mutex mut;
class Foo
{
public:
Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0)
{}
void update()
{
int count = 0;
{
std::lock_guard<std::mutex> lock(mut);
count = m_count;
}
std::cout << "count: " << count << std::endl;
}
void operator()()
{
while (true)
{
{
std::lock_guard<std::mutex> lock(mut);
m_count++;
}
std::this_thread::sleep_for(m_delay);
}
}
private:
std::chrono::milliseconds m_delay;
int m_count;
};
Foo obj(std::chrono::milliseconds(200));
int main()
{
std::thread t(std::ref(obj));
while(true)
{
obj.update();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
t.join();
return 0;
}
Alternatively, use an atomic variable so you don't need the mutex:
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>
class Foo
{
public:
Foo(std::chrono::milliseconds m) : m_delay(m), m_count(0)
{}
void update()
{
std::cout << "count: " << m_count << std::endl;
}
void operator()()
{
while (true)
{
m_count++;
std::this_thread::sleep_for(m_delay);
}
}
private:
std::chrono::milliseconds m_delay;
std::atomic<int> m_count;
};
Foo obj(std::chrono::milliseconds(200));
int main()
{
std::thread t(std::ref(obj));
while(true)
{
obj.update();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
t.join();
return 0;
}