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

Can threads share memory?

Started by smart_idiot Feb 17, 2005 at 9:06 PM 17 replies 1.4k views
Original Post
smart_idiot
smart_idiot
I haven't found anything that blurts out at me that the world is going to end if I do this, but I'm sure I heard someone say it, so I'm going to ask. Is it safe for Thread A to allocate some memory, terminate, and have that memory used and then later released by Thread B? I'm mostly concerned about Win32 at the moment, but any quirks with POSIX threads would be of interest, too.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
Deyja
Deyja
Of course. All threads started by the app share the same heap. Pointers and references to objects on the stack of another thread are invalidated when that thread ends, though. What you have heard people tell horror stories about is uncontrolled access to shared resources. If two threads access the same resource at the same time nasty things can happen. This is why serialization objects (mutexs, semaphores, and events under windows) exist.
smart_idiot
smart_idiot
Quote:
Original post by Deyja
Pointers and references to objects on the stack of another thread are invalidated when that thread ends, though.


Could you elaborate on this point? Do you mean references to objects on the stack of the dying thread? I expected that much. I'm not worried about synchronization, I think I have a good idea of how that's supposed to work.

If that's all there is to it, then I guess I'm good to go.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
LessBread
LessBread
The process "owns" the memory more properly termed the address space. The process also "owns" the threads. Each thread has it's own stack. Thread B will need a pointer to the memory allocated by Thread A to use it or release it. A global variable will probably do the trick. That is one area where serialization comes into play. Such a global variable would constitute a shared resource (per above). For a global variable a CriticalSection is the sych object to use.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
smart_idiot
smart_idiot
I'm going to rate you two up and hopefully do something useful with this knowledge in the morning that won't result in strange crashes, lockups, or data corruption. Thanks again.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
anonuser
anonuser
well if the thread is no longer working on that memory and the thread dies then its safe to operate on it. But i believe that memory will be invalidated so you'll need to copy the memory in to thread B in order for you to keep the data. At least that's what I am lead to believe someone correct me if im wrong.

you need to be careful when working with static buffers just because that's the rules and there's no gaurentee of data validity.

From what I know Win32 is threadsafe but its up to the programmer to keep it that way.

and most of the standard C library is thread safe unless otherwise noted and they usually have reentrant function alternatives. none come to me off the top of my head.
Evil Steve
Evil Steve
Quote:
Original post by anonuser
and most of the standard C library is thread safe unless otherwise noted and they usually have reentrant function alternatives. none come to me off the top of my head.
One example I can think of is strtok(), which is evil anyway ;)
capn_midnight
capn_midnight
Quote:
Original post by anonuser
and most of the standard C library is thread safe unless otherwise noted and they usually have reentrant function alternatives. none come to me off the top of my head.

to the contrary, most of the commonly used c library is NOT thread safe.
LessBread
LessBread
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
smart_idiot
smart_idiot
Quote:
Original post by LessBread


I agree, I think.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
Cold_Steel
Cold_Steel
Quote:
Original post by anonuser
well if the thread is no longer working on that memory and the thread dies then its safe to operate on it. But i believe that memory will be invalidated so you'll need to copy the memory in to thread B in order for you to keep the data. At least that's what I am lead to believe someone correct me if im wrong.
Actually, the way I understand it, is that a thread doesn't have its own memory, it uses the memory of the process that created it. Thus, if thread A mallocs 1000 bytes and then exits, that 1000 bytes will be inuse until either the process terminates, or someone else calls a free on that memory location. Thread termination does not free resources created within the thread. Please correct me if I'm wrong though.

Also, (to the best of my knowledge), calls such as new, delete, malloc, and free are all thread safe (how could they not be?). Unfortunately, the STL isn't thread safe, which caused me extra work...
______"Man is born free, and everywhere he is in chains" - J.J. Rousseau
smart_idiot
smart_idiot
I hope that's how memory works, I'm going to have a huge mess to clean up if it isn't. Everything seems to be working okay so far, though.

I wrapped everything up in classes, and I think I even managed to make it exception safe.

In short, my code looks something like this at the moment:
#include "mutex.hpp"class Foo {  protected:   // Mutex keeps track of which thread owns it, how many times it's been locked, etc.   Mutex mutex;    public:   doSomething()    {     // A MutexLock object will lock a mutex when it's created, and unlock it when it's destroyed.     // This hopefully ensures it gets unlocked even if an exception gets thrown.     MutexLock lock(mutex);     // do stuff here.          throw std::logic_error("6*9==42");    } };


And there I have a class that I can hopefully use safely in multiple threads without too much worry about bad things happening.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other
markr
markr
Free store, i.e. that provided by malloc() and new() etc, should be shareable safely by threads - it does not need to be freed by the same thread as allocated it (although this is USUALLY a good idea).

Stack - each thread has its own stack, although they are almost certainly accessible by other threads. But because a stack-allocated variable is only valid until it falls out of scope, there is no way for another thread to know when it falls out of scope. Therefore it's generally a BAD idea to use memory from another thread's stack.

Globals / statics - these are shared by all threads. Seeing as they don't stop being valid until main() ends, you should be ok. All subsiduary threads should stop before main() exits in a normal program.

That's to say - the main thread (the one which runs main()) should wait for all the others to terminate before returning from main() itself - otherwise destructors might get called for things still being used by other threads (actually, the results would be pretty random and rather platform-specific, but always undesirable).

As an aside - if an object creates a thread at some point during its lifetime, and in its destructor, tells that thread to exit and waits for it to do so, it is safe for that thread to access the object. This is of course true EVEN if it is allocated from the stack because the destructor is always called before it falls out of scope.

Mark
iMalc
iMalc
Yes that will be fine. What you don't want to be doing is allocating in a dll and freeing from the exe for example. In this case since it's all within the exe (I presume) so you'll be fine.
You could always ask the thing that is going to free it later to alocate it for you as well. That might be a cleaner design too.
ZedFx
ZedFx
heap memory is accessible by all threads, stack memory pertains to the specific thread

eg. (heap memory)

void * myheapmem = 0;int toggleheapmem(){		if (myheapmem)	{			free(myheapmem);		myheapmem = 0;		return 0;			}		myheapmem = malloc(1024);	return 1;	}


this code demonstrates the allocation and deallocation of heap memory, the 1st, 3rd, 5th ... threads which pass through the function allocate memory and the 2nd, 4th, 6th ... threads which pass through this function deallocate it.

eg. (stack memory)

int passingthreadnum = 0;int stacktest(){		int stackmem;		if (stackmem == 0)	{			stackmem = 5;		Sleep(0);			}	else	{			stackmem = 0;		Sleep(0);			}	}


when every thread passes through this function, stackmem will be uninitialised for each one, it will never be 0, even though the likely path of the function will lead it to the 'else' clause.

meaning:
- the first thread passes through and goes onto the 'else' section (for example)
- it sets stackmem = 0 and the sleeps relinquising control to the processor.
- The processor then decides to let a second thread begin this function
- when the second thread tests stackmem, it tests the stackmem variable on the 2ND threads stack (ie its own) NOT the 1ST. so it will also read it as uninitialised.
LessBread
LessBread
For some unknown reason it seems that the server doesn't like the message I intended to post.
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Deyja
Deyja
People keep repeating me. I feel famous!

Quote:
For a global variable a CriticalSection is the sych object to use.


Critical sections are often 'good enough', and are the lightest-weight object available. Real mutext objects, however, have considerably more power. Specifically, you can lock multiple mutexes in a single atomic operation.
ZedFx
ZedFx
* repeating with simplification ;-)

I prefer critical sections.
anonuser
anonuser
most of the standard C library is thread safe with a few non-reentrant functions. like steve said strtok and ctime is another. Work arounds are not hard.


http://ecos.sourceware.org/docs-2.0/ref/libc-thread-safety.html

they list the non-reentrant functions.

Topic Locked

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

Sign in to reply to this topic.