Opened 12 years ago

Closed 12 years ago

#4531 closed Bugs (fixed)

[Thread] ::promise shared between threads race-condition

Reported by: thijsterlouw@… Owned by: Anthony Williams
Milestone: Boost 1.44.0 Component: thread
Version: Boost 1.43.0 Severity: Showstopper
Keywords: promise thread future lazy_init Cc:

Description

I believe I have discovered a problem with boost::promise when it's shared via threads. I described the problem (informally) on the boost-users-list.

As suggested by Anthony Williams I raise a ticket for this bug here. Another anonymous helper also suggested that I create a standalone test-case for my bug, which I have done as well. You can find it attached.

The test case can be made like this:

g++ -O0 -g -static -I /usr/local/boost_1_43_0 boostpromise.cpp -o boostpromise -L/usr/local/boost_1_43_0/stage/lib/ -lboost_thread -lpthread

run like this: ./boostpromise

it will do an (endless) loop and usually results in an error (but no crash) or a core-dump (about 50-50 it seems). Either happens usually before several hundred iterations. Expected output is:

...
[11805] ok : 11815 
[11806] ok : 11816 
[11807] ok : 11817 
[11808] ok : 11818 
[11809] ok : 11819 
[11810] ok : 11820 
[11811] ok : 11821 
...

When I change the boost file /usr/local/boost_1_43_0/boost/thread/future.hpp and add lazy_init(); in the promise constructor (around line 919), the boostpromise program runs for a million iterations without problems. I guess this is not the best solution, because it's not lazy anymore, but solves my problem for now.

promise(): 
    future(),future_obtained(false)
{
    lazy_init();
}

back trace from coredump below:

(gdb) bt
#0  0xffffe410 in __kernel_vsyscall ()
#1  0x08105f78 in raise ()
#2  0x080d81b2 in abort ()
#3  0x080d3c3d in __assert_fail ()
#4  0x08060a4f in ~mutex (this=0x8174330) at /usr/local/boost_1_43_0/boost/thread/pthread/mutex.hpp:46
#5  0x08060ab7 in ~future_object_base (this=0x8174320) at /usr/local/boost_1_43_0/boost/thread/future.hpp:104
#6  0x08060b87 in ~future_object (this=0x8174320) at /usr/local/boost_1_43_0/boost/thread/future.hpp:290
#7  0x0804cc6c in boost::checked_delete<boost::detail::future_object<Packet> > (x=0x8174320)
    at /usr/local/boost_1_43_0/boost/checked_delete.hpp:34
#8  0x0804dd04 in boost::detail::sp_counted_impl_p<boost::detail::future_object<Packet> >::dispose (this=0x81742d8)
    at /usr/local/boost_1_43_0/boost/smart_ptr/detail/sp_counted_impl.hpp:78
#9  0x08048894 in boost::detail::sp_counted_base::release (this=0x81742d8)
    at /usr/local/boost_1_43_0/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:145
#10 0x080488da in ~shared_count (this=0xbf8d7fc4) at /usr/local/boost_1_43_0/boost/smart_ptr/detail/shared_count.hpp:217
#11 0x080492d2 in ~shared_ptr (this=0xbf8d7fc0) at /usr/local/boost_1_43_0/boost/smart_ptr/shared_ptr.hpp:169
#12 0x08051aa5 in boost::shared_ptr<boost::detail::future_object<Packet> >::reset<boost::detail::future_object<Packet> > (this=0x8171f68, 
    p=0x8174260) at /usr/local/boost_1_43_0/boost/smart_ptr/shared_ptr.hpp:392
#13 0x08061397 in boost::promise<Packet>::lazy_init (this=0x8171f68) at /usr/local/boost_1_43_0/boost/thread/future.hpp:909
#14 0x080616e8 in boost::promise<Packet>::get_future (this=0x8171f68) at /usr/local/boost_1_43_0/boost/thread/future.hpp:978
#15 0x08048425 in main () at boostpromise.cpp:70
(gdb) 

ps, attaching the test file gave a TRAC error (

OperationalError: database is locked

so I just inline it here, boospromise.cpp :

#include <boost/thread.hpp>

class Packet
{
	public:
		Packet(int i) : in(i) {}
		int in;
};

typedef boost::promise< Packet > packet_promise;
typedef boost::shared_ptr< packet_promise > packet_promise_shared_ptr;
typedef std::pair< unsigned long, packet_promise_shared_ptr > seq_promise_pair;
typedef std::map< unsigned long, packet_promise_shared_ptr > seq_to_promise_map;

class Demo
{
	private:
		boost::thread setvalue_thread_;
		seq_to_promise_map mymap_;
		boost::mutex mymap_mutex_;
		unsigned long counter_;
		
	public:	
		Demo() :
			counter_(10),
			setvalue_thread_(boost::bind(&Demo::do_calc_in_thread, this))
		{
		}

	  	packet_promise_shared_ptr calc()
	  	{
	  		packet_promise_shared_ptr ptrPromisePacket(new packet_promise());
			boost::mutex::scoped_lock l(mymap_mutex_);
			mymap_.insert(seq_promise_pair(counter_, ptrPromisePacket) );
			counter_++;
			return ptrPromisePacket;
	  	} 

		//calculate in a seperate thread and set_value(), loops forever
		void do_calc_in_thread()
		{
			while(true)
			{
				{
					boost::mutex::scoped_lock l(mymap_mutex_);
					seq_to_promise_map::iterator value_in_map_iter = mymap_.begin();
					for (value_in_map_iter; value_in_map_iter != mymap_.end(); value_in_map_iter++)
					{
						unsigned long in_counter = value_in_map_iter->first;
						//printf("[%d] thread running set value\n", in_counter);
						Packet resultPacket(in_counter);
						value_in_map_iter->second->set_value(resultPacket);			//make a copy
						mymap_.erase(value_in_map_iter);
					}
				}
			}
		}
			
};

int main(int argc, char** argv)
{
	Demo demo;
	unsigned long testCounter=0;

	while(true)
	{
		packet_promise_shared_ptr packetPromisePtr = demo.calc();
		boost::system_time const timeout = boost::get_system_time() + boost::posix_time::milliseconds(500);
		boost::unique_future< Packet > pf = packetPromisePtr->get_future();	//often causes core-dump
		
		if (!pf.timed_wait_until(timeout))			
		{
			printf("[%d] error \n", testCounter);
			break;
		}
		else
		{
			Packet result = pf.get();
			printf("[%d] ok : %d \n", testCounter, result.in);
		}
		testCounter++;
	}
	
}

Attachments (1)

boostpromise.cpp (2.2 KB ) - added by thijsterlouw@… 12 years ago.

Download all attachments as: .zip

Change History (2)

by thijsterlouw@…, 12 years ago

Attachment: boostpromise.cpp added

comment:1 by Anthony Williams, 12 years ago

Resolution: fixed
Status: newclosed

Fixed on trunk, revision 66146

Note: See TracTickets for help on using tickets.