Search code examples
c#referencefortrancancellation

Changing reference parameter value in fortran from calling function in c#


I have a dll written in fortran with a subroutine that takes a logical parameter as input. I want to use this to control cancellation of the subroutine, and have the possibility to change it's value in the calling c# code. Below is a small example of what I have tested with.

C# code:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace TestConsApp
{
    internal static class Program
    {
        private static bool _b;
        [DllImport(dllName: "TestFortran.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void TestBool(ref bool b);


        private static void Main(string[] args)
        {    
            TestFortranBool();
            Console.ReadLine();
        }


        private static void TestFortranBool()
        {
            _b = true;
            var t = Task.Factory.StartNew(() => TestBool(ref _b));
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = false;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = true;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = false;
            Console.WriteLine($"C# :{_b}");
            Thread.Sleep(5000);
            _b = true;
            Console.WriteLine($"C# :{_b}");

            t.Wait();
        }
    }
}

and fortran code:

module FortranTesting
  use iso_c_binding
  implicit none
contains
  
  
  subroutine TestBool(b)

  ! Expose subroutine TestFortran to users of this DLL
  !
  !DEC$ ATTRIBUTES DLLEXPORT::TestBool
  !DEC$ ATTRIBUTES DECORATE,ALIAS:"TestBool" :: TestBool
  
  logical(c_bool),intent(in) ::  b
  write(*,*) "Inside fortran"
  write(*,*) "F :", b
  call sleep(5)
  write(*,*) "F : ", b
  call sleep(5)
  write(*,*) "F : ", b
  call sleep(5)
  write(*,*) "F : ", b
  
  end subroutine TestBool
end module FortranTesting

What I get from this is:

C# :True
Inside fortran
F : T

C# :False
F : T

C# :True
F : T

C# :False
F : T

C# :True

What I would like is that F (fortran) alternates with C#. I have tried the opposite when reading the variable from C# and changing the variable in Fortran, which works as expected.


Solution

  • I have finally found an way to solve the cancellation. I give an exampel code that returns a value to the fortran code (and hence can be used to cancel the fortran subroutine/function):

    c# code:

    using System;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace TestConsApp
    {
        internal static class Program
        {
            [DllImport(dllName: "TestFortran.dll", CallingConvention = CallingConvention.Cdecl)]
            internal unsafe static extern void TestCallBack([MarshalAs(UnmanagedType.FunctionPtr)] ActionRefInt callBack);
    
            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
            public delegate void ActionRefInt(ref int i);
    
    
            private static void Main(string[] args)
            {
                TestCallBack();
                Console.ReadLine();
            }
    
            private static void TestCallBack()
            {
                var callbackHandler = new ActionRefInt(OnCallBackInt);
                TestCallBack(callbackHandler);
            }
    
            private static void OnCallBackInt(ref int i)
            {
                Console.WriteLine($"C#: Value before change: {i}");
                i++;
                Console.WriteLine($"C#: Value after change: {i}");
            }
    
        }
    }
    

    And Fortran code:

      module FortranTesting
      use iso_c_binding
      implicit none
    
      contains
    
      subroutine TestCallBack(callBack)
      !DEC$ ATTRIBUTES DLLEXPORT::TestCallBack
      !DEC$ ATTRIBUTES DECORATE,ALIAS:"TestCallBack" :: TestCallBack
      external        ::  callBack
      integer         ::  test
      
      test  = 1
      write(*,*) "F: Test before callback: ", test
      call callBack(test)
      write(*,*) "F: Test after callback: ", test
      end subroutine TestCallBack
      end module FortranTesting
    

    This code produce the following output:

    F: Test before callback: 1

    C#: Value before change: 1

    C#: Value after change: 2

    F: Test after callback: 2