Page 1 of 1

Thread and dll problem

Posted: Mon Nov 06, 2006 1:13 pm
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?

Posted: Mon Nov 06, 2006 2:03 pm
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.

Posted: Mon Nov 06, 2006 2:09 pm
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

Posted: Mon Nov 06, 2006 2:15 pm
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.

Posted: Mon Nov 06, 2006 2:17 pm
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.

Posted: Mon Nov 06, 2006 2:28 pm
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.

Posted: Mon Nov 06, 2006 2:44 pm
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.

Posted: Tue Nov 07, 2006 2:03 pm
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 ;)

Posted: Tue Nov 07, 2006 3:24 pm
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.

Posted: Tue Nov 07, 2006 3:37 pm
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.

Posted: Tue Nov 07, 2006 7:04 pm
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.)

Posted: Tue Nov 07, 2006 7:43 pm
by Shannara
But isnt the other problem is that you cannot return BSTR? Only accept them as parameters?

Posted: Tue Nov 07, 2006 7:51 pm
by Trond
I need to wait for threadsafe before I can continue anyways, is there any timeline for when it will be fixed?

Posted: Tue Nov 07, 2006 10:00 pm
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