Main Content

CWE Rule 543

Use of Singleton Pattern Without Synchronization in a Multithreaded Context

Since R2024a

Description

Rule Description

The software uses the singleton pattern when creating a resource within a multithreaded environment.

Polyspace Implementation

The rule checker checks for the issue Use of singleton pattern not thread-safe.

Examples

expand all

Issue

This issue occurs when both of these conditions are true :

  • You use a singleton design pattern in a multithreaded context.

  • The threads are not synchronized and It remains possible that multiple threads create more than one singleton objects in the program.

Polyspace® considers a class as a singleton if the class meets these criteria:

  • Its constructor is not public. That is, direct construction of the class is prohibited.

  • Its copy constructors and copy assignment operators are deleted. That is, the class is not copyable.

  • It has a static method that returns a pointer to the only instance of the class. This static method is then defined outside the class.

There are other possible singleton design patterns in C++ that do not meet all three preceding criteria. Polyspace does not report violation of this rule if you use these other singleton design patterns.

Risk

Using a thread-unsafe singleton design pattern can result in the creation of more than one singleton object in the program, which is unexpected.

Fix

To fix this rule violation, synchronize threads so that multiple singleton objects cannot be created.

Extend Checker

Extend this checker to check for thread-unsafe operations that Bug Finder might not detect by default. You might be using multithreading functions that are not supported by Polyspace. Extend this checker by mapping the functions of your multithreading functions to their known POSIX® equivalent. See Extend Concurrency Defect Checkers to Unsupported Multithreading Environments.

Example — Use of Singleton Class Not Thread-Safe

The class S in this example implements a singleton design pattern. The singleton class is then used in a multithreaded context. The two threads t1 and t2 are not synchronized. It is possible that the two threads create two instances of the singleton S, if a context switch happens in one of the tasks after the test (singleton_ == nullptr) but before the creation of the singleton instance.

#include <thread>
#include <unistd.h>
#include <mutex>


class S
{

	// constructor not public
protected:
	S(int value): value_(value)
	{
	}

	static S *singleton_;

	int value_;

public:

	//cloning prohibited
	S(S &other) = delete;

	void operator=(const S &) = delete;

	// static method that returns a pointer to the only instance
	static S *GetInstance();

	// helper functions
	void incr(void) {
		value_++;
	}
	int value() {
		return value_;
	}
};

S *S::singleton_ = nullptr;;


S *S::GetInstance()
{
	// check if instance exists. If not, create the only instance
	// and place it in the static object singleton_
	if(singleton_ == nullptr) {
		singleton_ = new S(0);  //Noncompliant
	}
	return singleton_;
}

void task1() {
	S::GetInstance()
	->incr();
}

void task2() {
	S::GetInstance()
	->incr();
}


void run() {
	std::thread t1(task1);
	std::thread t2(task2);
	while(S::GetInstance()->value() < 2) {
		sleep(1);
	}
}

Because the threads are unsynchronized, the use of the singleton design pattern is not thread-safe. Polyspace reports a violation.

Correction — Synchronize Threads

One possible correction is to use mutexes to synchronize the threads.

#include <thread>
#include <unistd.h>
#include <mutex>


class S
{

	// constructor not public
protected:
	S(int value): value_(value)
	{
	}

	static S *singleton_;

	int value_;

public:

	// cloning prohibited
	S(S &other) = delete;

	void operator=(const S &) = delete;

	// static method that returns a pointer to the only instance
	static S *GetInstance();

	// helper functions
	void incr(void) {
		value_++;
	}
	int value() {
		return value_;
	}
};

S *S::singleton_ = nullptr;;


S *S::GetInstance()
{
	// check if instance exists. If not, create the only instance
	// and place it in the static object singleton_
	if(singleton_ == nullptr) {
		singleton_ = new S(0);  //Compliant
	}
	return singleton_;
}

void task1(std::mutex* m) {
    std::lock_guard<std::mutex> l(*m);
    S::GetInstance()  
        ->incr(); 
}

void task2(std::mutex* m) {
    std::lock_guard<std::mutex> l(*m);
    S::GetInstance()  
        ->incr();
}

void run() {
    std::mutex m;
    std::thread t1(task1,&m);
    std::thread t2(task2,&m);
    while(S::GetInstance()->value() < 2) {
        sleep(1);
    }
}

Check Information

Category: Others

Version History

Introduced in R2024a