Singleton class. Getting std::system_error error.

Hi ppl :),

I am trying to write a singleton class using smart pointers but getting the following run time error :

terminate called after throwing an instance of 'std::system_error'
what(): Unknown error -1
Aborted (core dumped)

I am not able to comprehend why I am getting this error, since the same logic works if I use raw pointers.

Here is the code :


singleton.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef _SINGLETON_H_
#define _SINGLETON_H_

#include <mutex>
#include <memory>

class Singleton
{
    public:
        static std::shared_ptr<Singleton> GetSingleInstance();

    private:
        static std::shared_ptr<Singleton> singleInstance;
        static std::once_flag onceFlag;
        Singleton() = default;
        Singleton(const Singleton&) = delete;
        Singleton& operator =(const Singleton&) = delete;
};

std::shared_ptr<Singleton> Singleton::singleInstance;
std::once_flag Singleton::onceFlag;

#endif 



singleton.cpp

1
2
3
4
5
6
7
8
9
#include "singleton.h"

Singleton::Singleton() {}

std::shared_ptr<Singleton> Singleton::GetSingleInstance()
{
    std::call_once(onceFlag, [&] () -> void { Singleton::singleInstance.reset(new Singleton{});});
    return Singleton::singleInstance;
}



main.cpp

1
2
3
4
5
6
7
8
#include "singleton.h"

int main()
{
    std::shared_ptr<Singleton> s1 = Singleton::GetSingleInstance();

    return 0;
}



g++ version 6.3.0 20170519 (Ubuntu/Linaro 6.3.0-18ubuntu2~14.04)

compilation and run commands used :
1) g++ -C singleton.cpp -fpic -shared -o libsingle.so
2) g++ -L/home/kapil/singleton test.cpp -o test -lsingle
3) ./test //error occurs after this command

Appreciate any help :)

Thanks
Last edited on
$ g++ --version
g++ (Debian 6.3.0-18) 6.3.0 20170516

I think the GCC implementation may be buggy.

This code
1
2
3
4
5
6
7
8
9
10
#include <mutex>
#include <iostream>

std::once_flag f;

int main(){
	std::call_once(f, [](){ std::cout << "Hello, World!\n"; });
	std::call_once(f, [](){ std::cout << "Hello, World!\n"; });
	return 0;
}
throws.

The example from http://en.cppreference.com/w/cpp/thread/call_once prints
Simple example: called once
throw: call_once will retry
and then hangs.

It seems GCC's std::call_once() will throw if called from the main thread.

EDIT: I tried one more thing. My code from above does not throw if I pass -lpthread to g++.
Last edited on
> I am not able to comprehend why I am getting this error,

To use std::call_once() with the GNU library, we need to link with libpthread

The simplest is to specify -lpthread as a compiler option, and there won't be any run-time errors.
http://coliru.stacked-crooked.com/a/421281be0c30ca78
This is what is usually done:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <future>
#include <vector>
#include <mutex>

struct singleton
{
    static singleton& instance() ;

    private:
        singleton() { std::cout << "construct singleton\n" ; }
        ~singleton() = default ;

        singleton( const singleton& ) = delete ;
        singleton( singleton&& ) = delete ;
        singleton& operator= ( const singleton& ) = delete ;
        singleton& operator= ( singleton&& ) = delete ;
};

singleton& singleton::instance()
{
    // thread safe (edge case: unless the constructor throws
    // and the function is re-entered during the stack unwind)
    static singleton the_only_one ;
    return the_only_one ;
}

void foo( std::mutex& stdout_lock )
{
     const singleton& s = singleton::instance() ;

     {
         std::lock_guard<std::mutex> lock_it(stdout_lock) ;
         std::cout << "got singleton at address " << std::addressof(s) << '\n' ;
     }
}

int main()
{
    std::mutex m ;

    std::vector< std::future<void> > futures ;
    for( int i = 0 ; i < 10 ; ++i )
        futures.push_back( std::async( std::launch::async, foo, std::ref(m) ) ) ;

    for( auto& f : futures ) f.wait() ;
}

http://coliru.stacked-crooked.com/a/1e82aeb67cb579a1
Thanks alot JLBorges and Helios for taking time to analyze this and point out my mistakes :)

@JLBorges, though the implementation you suggested seems a bit scary :P :O, I'll try to understand future construct that you have used and find out how this program works.

Thanks :)
@JLBorges : I have a small query about the above code mentioned by you

We have defined the static variable "the_only_one" of type singleton in function "singleton& singleton::instance()" for the following reasons :

1) This helps us not to worry about initialization order which we would not be sure if "the_only_one" was a
static member of the singleton struct ?
2) This make the code thread safe except for the boundary case mentioned by you ?

If not so, then please let me know what's the advantage of not making "the_only_one" as static class
member function.

Thanks :)
Last edited on
> This helps us not to worry about initialization order

Yes.


> This make the code thread safe except for the boundary case mentioned by you ?

Yes. Technically, safe as long as control does not re-enter the declaration recursively while the variable (at block-scope with static storage duration) is being initialized.


9.7/4 Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

[ Example:
1
2
3
4
int foo(int i) {
  static int s = foo(2*i);      // recursive call - undefined
  return i+1;
}
— end example ]

Foot note: The implementation must not introduce any deadlock around execution of the initializer. Deadlocks might still be caused by the program logic; the implementation need only avoid deadlocks due to its own synchronization operations.
http://eel.is/c++draft/stmt.dcl


Also see: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Ri-singleton
Last edited on
Appreciate your help @JLBorges :)
Thanks :)
Topic archived. No new replies allowed.