Link to home
Start Free TrialLog in
Avatar of classmate
classmateFlag for Norway

asked on

Thread safe memory allocation

Hi!

If i want to allocate/release memory during the execution of a thread, will that automatically be thread safe?

If not, what ways could be used to make it safe in the easiest possible way?

hereunder:
TObject.Create / destroy
TComponent.Create / destroy
Setlength / refcount = 0
GetMem / ReallocMem / FreeMem


This could perhaps become a discussion rather than a question with exact answers.
In that case the best contributors will share points.

Feel free to participate :-)

classmate
Avatar of tobjectpascal
tobjectpascal

I'm not quite sure what you're getting at. Why not let the thread deal with memory allocation?
Avatar of classmate

ASKER

hi tobjectpascal!

If two threads try to allocate memory, say through windows API, at the same time, is there a chance that the program will crash?

And then, what if  two threads call TObject.Create at the same time?

and so on.



SOLUTION
Avatar of tobjectpascal
tobjectpascal

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I've never had problems, either, but is it actually guaranteed that .Create is safe?

The issue isn't with the new object that you're creating - it's whether the undelying memory manager that gives out the new memory you need is thread safe. Probably is, but where does it say that?
lol, you have a point...
Hi again, Objectpascal!
One problem with synchronize is that it depends on the main thread to process a message that in turn calls the synchronized method. Sometimes the message already being processed in the main vcl thread at the moment kan take so much time to finish, that waiting for it can be highly inefficient. Therfore i need another solution than Synchronize.

Also:
Given two simultanous calls to getmem that assigns to completely different variables. Will windows be able to do this safely, or do i have to make critical sections around all getmem calls within my program if i use threads?
hi andrewjb, that's exactly my point :-)

If TComponents are created, then there are TList objects involved also. This cannot be safe, since there also exists a TThreadList.

This code is taken from the Classes unit, Delphi 7

constructor TComponent.Create(AOwner: TComponent);
begin
  FComponentStyle := [csInheritable];
  if AOwner <> nil then AOwner.InsertComponent(Self);
end;

procedure TComponent.Insert(AComponent: TComponent);
begin
  if FComponents = nil then FComponents := TList.Create;
  FComponents.Add(AComponent);
  AComponent.FOwner := Self;
end;
So the my point is: Exactly what is thread safe, and what is not.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
What about Strings, dynamic arrays (setlength), and pointer types (new/dispose)
Again, I'd be happy to use them withouth problems, but can't give you a document to prove it...

Obviously, with all these, you have to protect each specific variable/array/pointer against simultaneous access if you're changing it from two thread, but you've already covered that..
Avatar of Wim ten Brink
Delphi has a thread-safe memory manager that will allocate and free memory for you. Any code that uses the Delphi functions to get and free memory will be using this memory manager and thus it will be thread-safe. However, if you load a DLL in your project, your DLL will be using a different memory manager, which could result in problems when e.g. the EXE creates an object and the DLL frees it again. When you do this, the EXE memory manager still doesn't know that the memory has been freed. However, the DLL memory manager never knew it was allocated. This is basically the biggest issue with memory allocation within Delphi.

Strings (huge strings, not the short strings) will cause the memory managers to get confused if you share them between EXE and DLL. So do objects and other pointer variables. But if you're not using any separate DLL's then there should be no problem.

Borland has been working on making more and more VCL components threadsafe and is doing a good job at it.

Making data threadsafe has nothing to do with getting and freeing memory, though. The problem is that two threads might be accessing the same variable. And if one thread is reading and the other is writing, it could lead to confusion. Look at the following pseudocode:

var AValue: string = 'Hello';

procedure Thread1;
begin
  AValue := 'Goodbye';
end;

procedure Thread2;
begin
  ShowMessage(AValue);
end;

Now, what could happen is that Thread1 is running and it changes the value. But halfway the change, the processor switches threads. Thus, Thread2 could be displaying something like: 'Goolo&$'. The length of the string has been set to 7 thus the extra 2 (random) characters and the first 3 characters have been replaced by the new value while the other two are still from the old value.
Now, this would be something that you would have to protect against. You have to prevent that multiple threads are doing something with the same data. Just compare it with giving a single small notebook to a class full of students and require them all to write notes in it. Those students will just tear it apart, each and everyone fighting for his own piece of notebook. It would be nicer to pass along the notebook from one student to the other, giving each his turn to write notes but it also means all other students will just have to wait their turn.
Would you like your students (threads) to fight over the data or just let them wait? :-)
hi Workshop_Alex!

>Delphi has a thread-safe memory manager that will allocate and free memory for you. Any code that uses the Delphi functions to get and free memory will be using this memory manager and thus it will be thread-safe.

This is the only interesting part of your comment (the rest i know about), but it is vital. Is there any documentation of this any place that you know of?
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Too slow this morning.... <g>

Russell

Hmmm.. That's scarey. I'd better check in the C++ world of Builder, since I currently create threads using CreateThread etc....

:-(
LOL@Russell

You are right by repeating me, though. People tend to ignore the IsMultiThread variable, yet they use CreateThread instead of BeginThread. Of course it is better to immediately set IsMultiThread to true when you know you're going to work on a multi-threaded application. (Or use BeginThread.)

I do wonder if many people use an alternative memory manager, though. I know there are a few alternative memory managers for Delphi but I think Delphi's solution is fast enough in most cases anyway. IsMultiThread is only used by the memory manager, though. And if you're 200% sure that your threads won't be allocating any memory then theoretically, you could set it to false again. It would speed up memory allocations in your main thread again a bit, but not really noticeable, I think.

@andrewjb, in C++ Builder you probably have the same issue. But if your thread doesn't allocate any memory then you're still safe. If you synchronise with the main thread and let the main thread allocate memory for you then you're safe again. If you use the Windows API to allocate memory blocks then this will probably be safe too. (Because Delphi just allocates it's own heap and the memory manager takes bytes from this heap.) And of course the chance of memory allocations colliding with one another aren't that big, unless you do a huge number of allocations. (Creating and freeing lots of objects in all threads.) Chances are that this "minor bug" in your code will cause an unexpected error only once in a while.
Compare it with a crossing in the road where all cars drive at the same speed, never look at the other traffic and never stop. (Every car is some memory manager call.) If you have 10 cars passing the crossing per minute from all directions, the chance of a collision still isn't very high. (If no car stays at the crossing more more than half a second.) Using IsMultiThread means that you put a traffic light at the crossing, thus forcing cars to stop when another car is going to cross.
I bet it's more common than you think...

Anyway, there's a compiler flag you should set in Builder, and need to link to a different library.. It's in the help somewhere.