Thread and dll problem

Just starting out? Need help? Post your questions and find answers here.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Thread and dll problem

Post by Trond »

I want to have a dll with a procedure that returns a string:

Code: Select all

ProcedureDLL.s Uppercase(S.s)
  ProcedureReturn UCase(S)
EndProcedure
Now, according to the manual, the returned string has to be declared as global:

Code: Select all

Global RetS.s
ProcedureDLL.s Uppercase(S.s)
  RetS = UCase(S)
  ProcedureReturn RetS
EndProcedure
The problem is, that this procedure can be called multiple times at the same time by various threads. Now this will obviously mess up the return value.

I can of course use a mutex to make sure that RetS is only used by one procedure at the time, but it wouldn't work because I have to release the mutex before I return, which means another thread will overwrite the return value of RetS before this threads can return it.
I obviously cannot release the mutex after I have returned.

How should I do this?
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

Could you not get the calling thread to pass a pointer to a buffer which it has allocated and which the dll would simply fill with the string to return? The dll could then return the number of characters placed into the buffer etc.

Alternatively, you could always look at 'thread local storage' but I'm not sure this would offer any advantages over the above method in the context of a dll.
I may look like a mule, but I'm not a complete ass.
User avatar
jqn
User
User
Posts: 97
Joined: Fri Oct 31, 2003 3:04 pm

Post by jqn »

Hi, my approach:

1.In every thread:

Code: Select all

structure mystruc
  mystring.s
endstructure

Global my{threadname}data.mystruc
....
callfuctionfast(dlladdr, @my{threadname}data)
2. In your DLL

Code: Select all

structure mystruc
  mystring.s
endstructure

ProcedureDLL Uppercase(*pointer)
  *pointer\mystring=Ucase(*pointer\mystring)
EndProcedure
Every structure name MUST BE distinct for every thread

Best Regards
JOAQUIN
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

srod wrote:Could you not get the calling thread to pass a pointer to a buffer which it has allocated and which the dll would simply fill with the string to return? The dll could then return the number of characters placed into the buffer etc.

Alternatively, you could always look at 'thread local storage' but I'm not sure this would offer any advantages over the above method in the context of a dll.
The problem with a buffer like that is that I don't know the length of the return value.
I still don't get why I have to return a global string, can this really be necessary?

jqn, the number of threads is dynamic.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

You coud set up the dll so that when called with a certain parameter = to NULL, the dll returns the number of characters required for the buffer. Then call it again having allocated a sufficiently large buffer etc. with this certain parameter containing a pointer etc.

Windows does this with quite a few API functions.
I may look like a mule, but I'm not a complete ass.
User avatar
jqn
User
User
Posts: 97
Joined: Fri Oct 31, 2003 3:04 pm

Post by jqn »

the number of threads is dynamic.
in this case you can use a macro for create the structure, like this:

Code: Select all

macro create_struc(trheadid)
  Global my#threadid#data.mystruc 
  callfuctionfast(dlladdr, @my#threadid#data) 
endmacro
(I'm not tested it with structures, but I'm use it with strings and other data types)

Structures allow the use of strings without define previously their length.
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

I can't use macro because the number of threads is dynamic! It's not known at compile time, it's not even known when the program starts.

I have currently decided to return a pointer to a buffer, which must then be freed by the calling application.

The problem is that I want it to be usable by C programs as well, and I don't know how C handles things like this.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

> The problem is that I want it to be usable by C programs as well, and I don't know how C handles things like this.

It doesn't. In C there is no such thing as a "string". Only buffers of characters.
So you cannot return a string in C, only the pointer to one. Where and how that
is stored is up to you.

There is no language independant standard of how to return a string from a function,
thats the reason for this whole mess. (otherwise PB could simply comply to this standard)

Basically what you need is to clearly define who allocates the string buffer and how,
and who has to free it.

There are 3 options: (well, 3 that make sense anyway)

1) The caller of the function creates the buffer and passes a pointer to it.
The good thing is it does not matter if a function is in a dll or anthing as the caller both allocates
and frees the buffer.
This is a very common way to do it in C.

The bad thing here is that the buffer size is limited (and the caller most does not know
the needed size in advance.)
For this, you would need to pass a length parameter and have a special return value
that tells that a bigger buffer is needed... all in all, more work.

2) The function allocates the string buffer, returns the pointer and forgets about it.
The caller has to free the string after using it to avoid memory leaks.
Good thing: the length problem is gone, and it is much less work.

Problem: The string must be freed the same way it was allocated. This is a bit of
a problem with PB, as PB uses HeapAlloc_() for AllocateMemory() with a private heap.
So the Dll and main program have different heaps. I know the windows memory functions are
quite forgiving about that one, so this may even work with different heaps, but it cannot be guaranteed.
Note that this is the advised way to handle it on windows (so PB does not go a weird way here),
it just is not so practical in the Dll problem.

Two solutions to that one: You can either use HeapAlloc_() yourself with a heap
like the process heap (GetProcessHeap_()), which the caller can also access to
call HeapFree(), or you can simply import the "malloc() and free()" from the C library
into PB and use these to allocate the memory. This is probably the best when
you want the Dll to be used from C as well.

Code: Select all

ImportC "msvcrt.lib"
  malloc(size)
  free(*memory)
EndImport
Now the Dll uses malloc() to get a buffer, put the string in and returns the pointer.
The caller simply calls free() and the memory is freed.

3) Use another String system such as the BSTR one.
This is not much different from solution 2, except that the BSTR system has its own functions to alloc/free a string.
Other than that, it is the same. The function allocates the string and the caller frees it. (SysAllocString_() / SysFreeString_())
The good point here is that some languages have native support for BSTR strings. (VB i think, and maybe also Delphi iirc)

The bad point is that it is unicode based, and if your Program/dll is ascii only,
you need to convert your strings always.


The way with the global variable in PB works, because then the string does not need to be freed.
It simply stays there until the next function uses the global variable to return a string. (returning the pointer to the global variable)
It is obviously not threadsave though.

These are the most practical solutions to this problem imho.
As you see, there is no common way of doing it, thats where all the trouble comes from.

I would go for solution 2 with the malloc() and free(). It is not so hard to do,
and especially C programmers will be quite comfortable with it ;)
quidquid Latine dictum sit altum videtur
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Thank you for the information, I think I'll use malloc and free as you suggested. Currently I used AllocateMemory() but I didn't think about the different functions used to free memory.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Post by srod »

The agony of choice! I too am looking at this problem and have followed this thread with interest.

3 good solutions.

I kind of favoured the first, but must admit that the use of Malloc() is very appealing.

How exactly does Malloc() allocate the memory? Does it simply use HeapAlloc_() etc?

Thanks.
I may look like a mule, but I'm not a complete ass.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

> How exactly does Malloc() allocate the memory? Does it simply use HeapAlloc_() etc?

I have no idea. The reference for the Microsoft C runtime does not mention it.
http://windowssdk.msdn.microsoft.com/en ... kz86d.aspx


Another way just came to mind: You could also simply export a function from your
dll to free the string memory. Then you can use whatever allocation method you like.
(as both allocation and freeing is done inside the dll.)
quidquid Latine dictum sit altum videtur
Shannara
Addict
Addict
Posts: 1808
Joined: Thu Oct 30, 2003 11:19 pm
Location: Emerald Cove, Unformed

Post by Shannara »

But isnt the other problem is that you cannot return BSTR? Only accept them as parameters?
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

I need to wait for threadsafe before I can continue anyways, is there any timeline for when it will be fixed?
Konne
Enthusiast
Enthusiast
Posts: 434
Joined: Thu May 12, 2005 9:15 pm

Post by Konne »

I'm not content with this Topic in the Help too. So my suggestion:
http://www.purebasic.fr/english/viewtopic.php?t=24487
Apart from that Mrs Lincoln, how was the show?
Post Reply