Skip to main content
GameDev.net gamedev.net
🔒 Locked

STL and multithreading - doesn't work

Started by LeoMaheo Aug 9, 2002 at 6:17 PM 4 replies 800+ views
Original Post
LeoMaheo
LeoMaheo
Hello I and a friend have written threads for the GBA, but we''ve discovered that the std-lib of our compiler isn''t thread safe (actually, the std-lib is STLport but without thread-support since there are no P-threads or SGI-threads etc. for the GBA). Therefore we have written a thread-safe allocator (it just uses our own versions of ''new'' and ''delete'' which disables thread-switching when the actual allocation of memory is done). We have tested oru ''new'' and ''delete'' a lot and know that they are thread-safe. But the STD-lib containers still crash even with our thread-safe allocator. A simple example:
  
class StupidThread : public Thread {
	typedef list<B, SafeAlloc<B> > SList;
	SafeAlloc<B> salloc;  // our thread-safe allocator

	SList slist;
public:
	StupidThread(char *name = "Stupid thread") : Thread(name), salloc(), slist(salloc) {}
	virtual void run() {	
		while (true) {
			slist.push_back(B());
			slist.pop_front();
		}  // Two threads like this running at once

	}          //    ---> crash !

};                 // One thread ---> OK (no crash)

  
Does anyone know how on earth the std-containers can be thread-unsafe even with thread-safe allocators ??? Are they coded in a really stupid way with static variables or what!, or are we doing something wrong? Thanks for any help
I like to write games.. and to do real sports.
dalleboy
dalleboy
Just a thought: A pointer to the memory allocated by the thread-safe allocator in the container may exist in two (or more) threads at the same time. If that is the case you should use some sort of synchronization before using the container.
dalleboy
dalleboy
That seems to be the case, at least with the STL-library that comes with VC++ 6.0:

  
template<class _Ty, class _A = allocator<_Ty> >
class list
{
public:
void pop_front()
{ erase(begin()); }
void push_back(const _Ty& _X)
{ insert(end(), _X); }
iterator insert(iterator _P, const _Ty& _X = _Ty())
{_Nodeptr _S = _P._Mynode();
_Acc::_Prev(_S) = _Buynode(_S, _Acc::_Prev(_S));
_S = _Acc::_Prev(_S);
_Acc::_Next(_Acc::_Prev(_S)) = _S;
allocator.construct(&_Acc::_Value(_S), _X);
++_Size;
return (iterator(_S)); }
iterator erase(iterator _P)
{_Nodeptr _S = (_P++)._Mynode();
_Acc::_Next(_Acc::_Prev(_S)) = _Acc::_Next(_S);
_Acc::_Prev(_Acc::_Next(_S)) = _Acc::_Prev(_S);
allocator.destroy(&_Acc::_Value(_S));
_Freenode(_S);
--_Size;
return (_P); }
};
TangentZ
TangentZ
Right... it looks like each StupidThread has its own
allocator. So how can you make sure they don''t return the
same piece of "allocated memory" in two or more threads?
Shouldn''t there be only one global instance (Singleton)?



Kami no Itte ga ore ni zettai naru!
神はサイコロを振らない!
LeoMaheo
LeoMaheo
Thanks for the help!
I am not really sure if I understood the problem you pointed out, but I think we already have thought about it. I''ll show below why I still think our code should be safe.


  
// Our allocator just redirects the allocation to our own

// thread-safe new :

pointer SafeAlloc::allocate(size_type num, const void* = 0) {
pointer p = (pointer)(::operator new(num*sizeof(T), safe_tag)
return p;
}

// and ''new'' is really thread-safe...

void* operator new(size_t size, SafeTag) {
ThreadSwitching::disable();
void *p = malloc(size); // <-- won''t be interrupted

ThreadSwitching::restore();
return p;
}

Since we disable thread-switching before any use of malloc, we should never have problems with a certain piece of memory being allocated by two allocators. Am I right ?

And, should it at all possible to supply with a thread-safe allocator and then get a thread-safe (etc...)? or are there other things with we haven''t thought of that will make the threads crash anyway ? Thanks for all answers
I like to write games.. and to do real sports.
Stoffel
Stoffel
STL containers are not internally synchronized. They must be externally synchronized. It says so right in the docs. Forcing all containers to be internally synchronized would cause an undue inefficiency, e.g. why should ever vector need to grab kernel mutexes when vector a is only used in a single thread.

Making an internally synchronized allocator might work for serializing access to allocations, but you're still going to have problems if one thread is writing to a contianer while another one is reading from it. There's no good way to internally synchronize that. You need to externally synchronize.

[edited by - Stoffel on August 10, 2002 2:56:51 PM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.