Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29252 Discusiones

Maybe a Challenging Threading Problem?

pecan204
Principiante
964 Vistas
Hi,

I need some advice on how best to tie program operations together. From what little I know maybe this is a multi thread problem. Some comments on this would be good.

I have a java gui that will launch a fortran dll through a c++ wrapper. I send a 1 from the java to start a while loop that will get the time and then I would like it to return the time but keep looping and returning the time to the java program for each loop. The program looks like below. Unfortunately once it gets the time below once it returns and ends. Thanks for any comments.

!Fortran DLL program
Subroutine DLL(start,time)

integer start
character*8 time

do while(start.eq.1)
time = clock()
endo

return
end DLL program

0 kudos
8 Respuestas
Jugoslav_Dujic
Colaborador Valioso II
964 Vistas
Yep, it looks like a good candidate for a thread. To convert it to a threadable procedure, you have to:

- Rewrite it so that it accepts only one argument. Since you apparently need two, you can pack it into a TYPE:
TYPE IO
CHARACTER(8) time
INTEGER start
END TYPE IO
...
struct IO {
char time[8];
int start;
};
Also, it should be an INTEGER FUNCTION, not a SUBROUTINE.

- From C++ side, replace the direct call with a call to CreateThread or _beginthread. Pass an &IO as lpParameter:

DWORD tid;
HANDLE hThread;
hThread = CreateThread(NULL, 0, DLL, &cio, 0, &tid)
CloseHandle(hThread);

- However, the gotcha is that you can get mis-synchronization, since writing to clock() is not atomic. Consider this case:

//C++ stopping code
IO cio;
...
cio.start = 0;
cout << cio.time;

When you set cio.start to zero, it does not mean the fortran will stop immediately; it will wait for its timeslice. As result, you may get an old value of cio.time, or, worse, << operation will happen simultaneously with execution of clock(), thus you'll get a garbage value... But let's discuss this later.

Jugoslav
pecan204
Principiante
964 Vistas
Yugoslav,

I see what the concept you are saying but admitidtly I am weak withh the user defind types and tying them to the loop. I know that structures ussuqally a formatted in a struct.variable format. Should I be specifying that or using a use for a module?

My attempt is in attached files. I also turned it into a C executqable calling a fortran dll.

Thanks for the help.

Ken
pecan204
Principiante
964 Vistas
Yugoslav,

I see what the concept you are saying but admitidtly I am weak withh the user defind types and tying them to the loop. I know that structures ussuqally a formatted in a struct.variable format. Should I be specifying that or using a use for a module?

My attempt is in attached files. I also turned it into a C executqable calling a fortran dll.

Thanks for the help.

Ken
Jugoslav_Dujic
Colaborador Valioso II
964 Vistas
A TYPE is semantically equivalent to C++ struct, but it is a variable like any other. Compare it with COMPLEX -- it just carries several parts within itself. No need for MODULEs here -- all you need the TYPE is for transport & package. That also means you have to match sizes and order of variables within the structure in C++ and Fortran:

INTEGER FUNCTION FTREND2 (tio)
USE DFPORT
IMPLICIT NONE
!DEC$ ATTRIBUTES DLLEXPORT :: FTREND2

TYPE IO
   INTEGER START           ! if 1 start timer
   CHARACTER*10 TimeIs
END TYPE IO

TYPE(io):: tio

  DO WHILE ( tio%START .EQ. 1)

    tio%timeIs = CLOCK ( )   ! Uses DFPORT

    tio%timeIs = tio%timeIs // char(0)

    write (6,*) tio%timeIs
    
    FTREND2 = STR
  
  ENDDO

ftrend2=0
RETURN
END FUNCTION FTREND2


C++ struct must have the same order of members. Note that I added 2 characters into timeIs to accomodate for CHAR(0). Since tio is effectively passed by reference, any change on tio%timeIs in Fortran is immediately visible in C++ (as if a COMMON was used). Do NOT use anything else than INTEGER function -- CHARACTER functions are tricky to get correct in mixed language, and the function result will be visible only when the thread ends anyway.

As I said, this code still has a synchronization problem, but let's go step by step.

Jugoslav
pecan204
Principiante
964 Vistas
Thanks Yugoslav,

I did get the fortran to compile as I see your points.

I think the createthread would then start the DLL loop by exposing the start variable memory, but it is not clear if I need to import the dll or somehow point to the memory? If the create thread does it, does the fact that I defined the struc with the same name automatically assume it is the same memory that the dll would use?

Also I don't see how a prototype needs to be written as well, you mentioned the create thread will replace the call to the DLL. In that case then do I need to import the dll?

I also set multi-threading in the project settings for code generation. This is what I now have. I get alot of errors in the c file.

#include
#include

struct io {
char time[10];
int start;
};

typedef struct io cio

extern "C" __declspec(dllimport) void _stdcall FTREND3
( long * );

void main (int argc, char *argv[])
{
io.start = 1;
int tid;
HANDLE hThread;

printf("Next start fortran function thread ");

FTREND3 ( &io.start);

hThread = CreateThread(NULL, 0, FTREND3, &cio, 0, &tid)
CloseHandle(hThread);

printf("In c after fortran call ");
printf("string = %s ",io.time);
}
Jugoslav_Dujic
Colaborador Valioso II
964 Vistas
Perhaps a confusion here occurred because I referred to "DLL" before only in the sense that you titled your subroutine "DLL". So, when I talked about "DLL" uppercase I meant "Fotran routine", i.e. FTREND now.

Think of CreateThread as (a special perhaps) just another way to say CALL. Thus, it needs the routine to be dllexported, to include .lib in C++ project... everything as usual etc.

Logic of your C code looks fine except that direct invocation of FTREND3 -- you can delete that line. The errors are probably consequence of

typedef struct io cio

1) You're missing a semicolon. 2) This does not mean what you wanted -- typedef says "io and cio are synonims". You want:

struct io cio;

Jugoslav
pecan204
Principiante
964 Vistas
Here is the latest.

I needed to add the window.h file and cast the function to a void pointer with this (LPTHREAD_START_ROUTINE)FTREND3 apparrently. See next para.

FTREND3 does not have the interface expected by CreateThread, as it accepts a long * where CreateThread wants a void *. In this particular case, you can simply force the cast:hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FTREND3, &cio, 0, &tid);

It then compiled. But would not work. It was mentioned that I need the createThread prototype which I added per my understanding and I get the error below.

I am not sure about the EXTERN definition and if that is correct. Thoughts on this and the prototype correctness would be good? Thanks for the help.


cinterface3.cpp(15) : error C2373: 'CreateThread' : redefinition; different type modifiers
Error executing cl.exe.

#include
#include
#include

struct io {
char time[10];
int start;
};

struct io cio;

/* prototype for createThread */

HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpAttr, DWORD dwStack,
LPTHREAD_START_ROUTINE lpFunc, LPVOID lpParam, DWORD dwFlags, LPDWORD lpdwID);


extern "C" __declspec(dllimport) void _stdcall FTREND3 ( long * );

void main (void)
{
DWORD tid;

HANDLE hThread;

cio.start = 1;

hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FTREND3, &cio, 0, &tid);

CloseHandle(hThread);
printf("string = %s ",cio.time);

}
Jugoslav_Dujic
Colaborador Valioso II
964 Vistas
Don't redeclare CreateThread -- C++ doesn't see the Fortran prototype anyway. What is being passed through is a pointer (in this case, to a structure io, but it's not substantial); every pointer can be casted to void* (which is equal to LPVOID). Just change the prototype of FTREND3 to FTREND3(void*);

Jugoslav
Responder