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

Calling a Fortran DLL from a multithreaded C++ program

ferrad1
New Contributor I
1,511 Views

I have a multithreaded C++ driver program calling my Fortran DLL.  It all works fine without file writing.  When I turn on file writing, all threads are using the same unit number and it crashes.  Is there any way (or indeed an example) to get the Fortran code to use unique file unit numbers for each thread it is been used in?

I am using Threaded (/reentrancy:threaded) and Debug Multithread DLL (/libs:dll /threads /dbglibs)

Update:  I used newunit= on the file open statement and that seems to solve the problem.  Whether that is by chance or not, I do not know.

0 Kudos
7 Replies
jimdempseyatthecove
Honored Contributor III
1,488 Views

newunit is a good way to handle this.

Does your DLL require you pass the unit number(s) out of the DLL to the calling code to be used later on subsequent call to the DLL? Or does it assume it is a hard-wired unit to the calling thread? For example held in variable names UIN and UOUT.

In the case of the latter, you might be able to get by with combining the newunit together with making your unit variables THREADPRIVATE.

 

Jim Dempsey

0 Kudos
ferrad1
New Contributor I
1,487 Views

What I was going to do next was to write Fortran code to append to the output file in subsequent calls to the DLL.  I don't think I need to use the same unit number for this, so I don't see the need to pass it back to the C++ calling thread.  I just open with a newunit for APPEND.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,477 Views

Presumably, the multiple threads are appending to their exclusive files.

Note, it is also presumed by your description that the threads are using unique file names (or unique folders).

If they intend to append to the same file, then (carefully in a thread safe manner) one of the threads would open the file to a given unit, and all the threads would share the unit/file.

integer, save :: ioUNIT = 0
...
if(ioUNIT == 0) then
  !$omp critical
  if(ioUNIT == 0) then
    open(newunit=ioUNIT, ...)
  endif
  !$omp end critical
endif
...
!$omp critical
... your write codes here
!$omp end critical
...
!$omp barrier
if(ioUNIT /= 0) then
  !$omp critical
  if(ioUNIT /= 0) then
    close(ioUNIT, ...)
    ioUNIT = 0
  endif
  !$omp end critical
endif
...

The above assumes each thread calls the dll once.

Some rework would be needed if the file is to be open across each thread's multple calls to the dll.

Jim Dempsey

 

0 Kudos
ferrad1
New Contributor I
1,476 Views

Thanks for this, I was planning to have each thread write / append to its own file, which has a unique name.  I thought having only one file would be too tricky.  But now I see your code above, I might have a go.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,455 Views

Additional note:

You may, or may not, want to add a FLUSH(ioUNIT) after you write your data (place just before !$OMP END CRITICAL).

As you've described your situation, the flush shouldn't be necessary. If the computation time is long, and you have a separate process monitoring the content (or size) of the data file, then the flush might come in handy to get a current view of the output file (size).

Also, keep in mind that, the !$omp barrier requires all threads of the parallel region reach the barrier.

IOW, if a particular thread has no work to do, do not return from a subroutine that has a barrier some time later in the code for the working threads (use and IF to branch around the DoWork section), and assure that the non-working thread encounters the barrier.

 

Jim Dempsey

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,451 Views

Note 2:

As the threads may enter the write section in non-determinant order, you may need to insert/annotate the blob of data being written.

If you require the data to be ordered, then you may be able to use the !$OMP ORDERED/!$OMP END ORDERED directives in place of the critical section surrounding your write code.

You will also need to supply the ORDERED clause to your !$OMP DO. The reason for this (I presume) is the !$OMP DO might partition the iteration into fewer threads than the size of the thread team for the parallel region. The !$OMP ORDERED behaves somewhat like an !$OMP CRITICAL that passes the threads (of the team that are participating in the !$OMP DO). 

Jim Dempsey

 

 

0 Kudos
ferrad1
New Contributor I
1,427 Views

Interesting, thank you.

0 Kudos
Reply