Attempting to port a C source written for POSIX to Windows

Pages: 12
Hi everyone,

I am trying to natively compile a program called 'MRCC' (a quantum chemistry program) in Windows 10. The source code of the program is mainly written in Fortran (which perform the mathematical calculations), and the command interface part is written in C. The fortran part compiles just fine on Windows, but the C part produces loads of problems. (Both with Intel C++ compiler, and with gcc-mingw64 compiler)

For example, this is the first C source that is causing the problem:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>


#ifdef __cplusplus
extern "C" {
#endif

#ifdef INT64
void mrccend_(long*);
void dmrccend_(long*);
long ERROR_SIGTERM = -1;
long ERROR_SIGSEGV = -2;
#else
void mrccend_(int*);
void dmrccend_(int*);
int ERROR_SIGTERM = -1;
int ERROR_SIGSEGV = -2;
#endif

void parent_sigterm_(int, siginfo_t *, void *);
void child_sigterm_(int, siginfo_t *, void *);
void child_sigsegv_(int, siginfo_t *, void *);
void sendSignalToChildren(int);

static struct sigaction old_act;

void signalinit_() {
// initialise dmrcc's responses to signals
   struct sigaction act_term;
   memset(&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &parent_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);
}

void parent_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise response to signal SIGTERM
   pid_t pid_parent;
   char pidchar[10];
   char command[40];

   pid_parent = getpid();
   sprintf(pidchar, "%d", pid_parent);

   printf("\n Program dmrcc recieved SIGTERM\n"); fflush(stdout);

   sendSignalToChildren(SIGTERM);
   sleep(5);
   sendSignalToChildren(SIGKILL);

   printf("\n Program dmrcc terminating\n"); fflush(stdout);
   dmrccend_(&ERROR_SIGTERM);
}

void sendSignalToChildren(int sig) {
   int ownPid = getpid();
   int pid, numPids = 0;
   int *pids;
   FILE *pidfile = fopen("pids", "r");
   if (pidfile == NULL) {
      printf("Error: Could not open pids file\n");
      return;
   }

   // number of running processes other than the current process
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         numPids++;
      }
   }
   rewind(pidfile);

   // read other process' PIDs
   pids = (int *)malloc(numPids * sizeof(int));
   int i = -1;
   while (fscanf(pidfile, "%d", &pid) != EOF) {
      if (pid != ownPid) {
         pids[++i] = pid;
      }
   }

   // send signal sig to processes
   printf("\n Sending signal %2d to child processes\n", sig); fflush(stdout);
   for (i = 0; i < numPids; i++) {
      kill((pid_t)pids[i], sig);
   }
   
   fclose(pidfile);
   free(pids);
}

void signalinitchild_() {
// initialise child's responses to signals
   struct sigaction act_term;
   memset (&act_term, '\0', sizeof(act_term));
   act_term.sa_sigaction = &child_sigterm_;
   act_term.sa_flags = SA_SIGINFO;
   sigaction(SIGTERM, &act_term, NULL);

   struct sigaction act_segv;
   memset (&act_segv, '\0', sizeof(act_segv));
   act_segv.sa_sigaction = &child_sigsegv_;
   act_segv.sa_flags = SA_SIGINFO;
   sigaction(SIGSEGV, &act_segv, &old_act);
}

void child_sigterm_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGTERM
   mrccend_(&ERROR_SIGTERM);
}

void child_sigsegv_(int signum, siginfo_t *siginfo, void *context) {
// initialise child's response to signal SIGSEGV
   mrccend_(&ERROR_SIGSEGV);
   if(old_act.sa_flags & SA_SIGINFO)
   {
      (*old_act.sa_sigaction)(signum, siginfo, context);
   } 
   else 
   { 
      (*old_act.sa_handler)(signum);
   }
}

#ifdef __cplusplus
}
#endif 


I am a chemist mainly, and don't have much experience with C programming. Nevertheless, after digging a bit I am almost certain that the problem is coming from the <signal.h> header file, and also from <unistd.h>. Intel C++ does not recognize unistd.h. The mingw64-gcc has a unistd.h but it throws an error at the first mention of 'siginfo_t *'. I think this is because the POSIX signal.h contains lots of other signalling options, that are implemented differently in Windows.

From what I can gather, the siginfo_t part gathers extra information when the program is aborted. Can you guide me to change the code to use the Windows native options (i.e. replacing signal.h and unistd.h)?

If that's not possible then how can I remove the extra information gathering parts (siginfo) without changing anything else, so that gcc can compile it?

Edit: Cygwin compilation with gfortran (and gcc) does not work. Is there any way to use cygwin POSIX compatibility dlls with Intel Fortran and C++ compilers?
Last edited on
While its probably possible to 'fix' it, the best option is to compile it as-is.
If your tool is not working, try installing cygwin and using that. I have had a great deal of luck porting small non-gui programs to windows with it.

I threw it in mine and I got missing functions for the mrccend_ etc. Which is fine; they are in another file you have (?) I assume (?)

But no complaints about the headers etc.
Last edited on
If you want to compile such code on windows, then you're going to need https://www.cygwin.com/

Cygwin is:
a large collection of GNU and Open Source tools which provide functionality similar to a Linux distribution on Windows.
a DLL (cygwin1.dll) which provides substantial POSIX API functionality.

The detail is going to be in 'substantial', and how many dark corners of signal handling have been implemented.

It's been many years since I last used cygwin, but it seemed robust enough even way back then.

Basically, you install cygwin, and choose the development tools to give you a c++ compiler that will have all the right header files for POSIX and more importantly, link against the DLL compatibility layer.

Unfortunately running through cygwin means everything has to pass through the cygwin layer before being executed, which would likely slow the program down. And this is a program where performance is vital.

Also, from cygwin I am not sure how I can use the Intel MKL to make linear algebra faster.
The only bits that are going to slow down are those POSIX calls which have no clean windows equivalents.

> And this is a program where performance is vital.

The source code of the program is mainly written in Fortran (which perform the mathematical calculations), and the command interface part is written in C.


None of this seems to be on any critical path.
I tried to compile the program with Cygwin using gfortran and gcc, however, now the fortran parts refuse to compile with gfortran. I am not sure why this is, but my blind guess is that it has something to do with the -assume:byterecl directive given to the Intel Fortran compiler, which doesn't have any equivalent for gfortran (?).
Last edited on
@salem c

>None of this seems to be on any critical path

Fair enough, what I tried to mean was that if I could compile it natively, I would use Intel compilers with MKL, which would give a massive speed boost. On cygwin, I would have to use gfortran and gcc compiler (and openBLAS presumably), and the code would not be optimized that much.

But all of that is moot now, as gfortran fails to compile the files.
Last edited on
Exactly what did your fortran compiler not like?

Use the same vendor for both C and Fortran and they should work and link fine:
gcc (or g++) and gfortran for the gnu compiler collection
cl (or whatever) and ifort for the intel compiler collection
However, don't attempt to mix a c compiler from one and a fortran compiler from another.

-assume:byterecl is a specific compiler directive, for one particular compiler; you can't expect it to be the same for any other compiler.

Find out why it's needed (if it's needed) and what it does.

I've just checked the ifort help, which confirms that something similar still exists:
/assume:<keyword>
          specify assumptions made by the optimizer and code generator
          keywords: none, [no]byterecl, [no]buffered_io, 
                    [no]buffered_stdout,
                    [no]bscc (nobscc same as /nbs), 
                    [no]contiguous_assumed_shape, [no]contiguous_pointer,
                    [no]cc_omp, [no]minus0,
                    [no]dummy_aliases (same as /Qcommon-args),
                    [no]ieee_fpe_flags, [no]fpe_summary,
                    [no]old_boz, [no]old_complex_align,
                    [no]old_inquire_recl,
                    [no]old_logical_ldio, [no]old_logical_assign,
                    [no]old_ldout_format, [no]old_ldout_zero,
                    [no]old_maxminloc, [no]old_unit_star, [no]old_xor,
                    [no]protect_allocates, 
                    [no]protect_constants, [no]protect_parens, 
                    [no]recursion, [no]realloc_lhs, [no]2underscore, 
                    [no]underscore (same as /us),
                    [no]std_intent_in, [no]std_minus0_rounding,
                    [no]std_mod_proc_name, [no]std_value,
                    [no]source_include,  
                    [no]split_common, [no]writeable_strings


Intel's online documentation gives
byterecl
    Determines whether units for the OPEN statement RECL specifier (record length) value in unformatted files are in bytes or longwords (four-byte units). 
Last edited on
I'm curious as to what all this signal handling is actually doing.

You seem to be sending SIGTERMs around as a means of implementing a controlled interrupt to some long running process, so that it can get to a checkpoint to save it's state.

Is there some other C code calling the Fortran (as a library), or is the Fortran a completely separate standalone program.
I have had no complaints about cygwin's c++ performance. Be sure to use -O3 or whatever optimization flags you like, but it makes a pretty hot native .exe file when its done. Don't have to use their silly bash shell, you can just use a cmd console in windows if you set your paths to the cygwin dll etc folder. The bash shell itself IS sluggish.
@lastchance the gfortran compiler throws multiple rank and type mismatch errors. I don't know anything about fortran so I can't be sure why Intel fortran does not throw errors.

@salem c, There are other C codes. The main launcher program is written in c, it launches the fortran program through MPI (not sure if the fortran part is standalone). I assume if the program is interrupted then the program will save whatever calculation it was doing maybe. Its a command-line program and I don't see where it will need checkpoints.

If you know how the signal system works, then can you tell me whether the code will run if I just remove all the references to siginfo ? Windows supports SIGTERM, but does not have SIGINFO.
@sm9,
You don't launch programs through MPI. It's an interface that allows you to run parallel computations on many processors with distributed rather than shared memory. You will need an MPI distribution that works with your compiler. On Windows I've managed to make Microsoft MPI work with the intel compiler ifort and partially with the gnu compiler gfortran. I've only got mpich to work with the intel compiler. I've never managed to make either work with the NAG compiler, despite that being one of the better fortran compilers. If you are using MPI then please state which implementation you are using.

If you are using MPI in Windows then I think you are safer with the intel compiler.

You can do mixed-language programming in c and fortran without either "launching" the other. They are simple subroutine or function calls. You just have to worry a little about call by value (c default) or by reference (fortran default), and also multidimensional array storage order.
Last edited on
@lastchance The program also allows OpenMP parallelisation, so I have opted not to use MPI.

So if I compiler the fortran parts with Windows intel fortran compiler, and the c parts with cygwin gcc compiler would that work? Another problem is that the final executable is prepared by linking fortran and c .obj files with fortran. I don't know if Windows Intel fortran can link to cygwin gcc .obj file.
I'm not convinced that you could mix and match object files for pure Windows and for cygwin - they are different environments.

For both Fortran and C work out what you can compile in which environment and with which compiler. You can worry about linking when you have sorted out compiling to object code. Note that compiler directives will be different for every compiler: you can't expect those to carry over. If the source code is standards-compliant then it should compile with any compiler.

The MinGW version of the gnu compilers includes OpenMP; I suspect (but don't know) that the intel compilers will have it available too.

You haven't adequately addressed @salem c's question. Is the c component calling fortran subroutines and functions, or is it using system calls to launch complete stand-alone programs (which could presumably also be started manually if desired)?
Last edited on
It looks like you can use cygwin to make a dll that you can use, or that you can force a windows dll to be called by cygwin. I have not tried it, just did a bit of web searching and from what I can tell its doable with some degree of success.

Not sure that you can do the same with straight up object files.
Last edited on
@lastchance I am not a programmer sorry, I don't know the answer to @salem c's question. What I can see is that the fortran and c files are all compiled to .obj files. Then the fortran compiler is again called to link all those .obj files together (c and fortran).

@jonnin For some reason compilation with gfortran is broken. Cygwin's gfortran throws multiple type and rank mismatch errors, which Intel fortran does not (for some reason??). So compiling with cygwin is not an option.
@sm9, is it this MRCC ?
https://mrcc.hu/index.php
sm9 wrote:
What I can see is that the fortran and c files are all compiled to .obj files. Then the fortran compiler is again called to link all those .obj files together (c and fortran).


Can you post "what you can see".

I did do some mixed-language compiling with the gnu compilers (from MinGW) in this post:
http://www.cplusplus.com/forum/general/266443/#msg1146615

With g++ or gfortran you can actually use either to compile and link both c and fortran ... as long as you state the appropriate libraries at link time. If you don't state any libraries explicitly then they will link the libraries appropriate to the implied compiler.

@jonnin For some reason compilation with gfortran is broken. Cygwin's gfortran throws multiple type and rank mismatch errors, which Intel fortran does not (for some reason??). So compiling with cygwin is not an option.

I undestand. However you can also make the dll with the non cygwin compiler and use the dll with the cygwin's c++: if I understood the drive-by searching I did on the subject, its possible. I have not done this, so I am relying on (who knows how old) internet info and I didn't dig in; my look was just to see if it was possible or not. You can take a look, see if it gets you closer.

you can also throw in the towel and compile it on linux and use DOCKER to move it to PC. Docker is an incredible resource hog, and it may not give you the performance you want, depending. Only way to know is to try, and its free to do that.

I realize we keep changing the tools and rules on you: we don't know what is going to work here, its a hands-on find the solution that works problem unfortunately.
Last edited on
@salem c , yes the MRCC program is from https://mrcc.hu/index.php

@lastchance, the program is compiled with a build script. I can post snippets here:

This part compiles the fortran routines:
1
2
            echo "Compiling $i with options $preprocopt $compileropts"
            $fortran $preprocopt $compileropts -c $ii || export bstat="no"

This c routines are compiled like this:
1
2
 echo "Compiling xalloc with options $ccompileropts"
 $ccompiler $ccompileropts -c $xalloc || export bstat="no"

Then linking is done with fortran:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if [ x$tt == "xyes" ];
then
    if (([ "x$extralibs" != "x" ]) || ([ "x$linpacklib" != "x" ]));
    then echo "Linking goldstone with libraries: $linpacklib $extralibs"
    else echo "Linking goldstone"
    fi
    $fortran $compileropts $ompopt -o goldstone goldstone.o mem.o xalloc.o combin.o signal.o qsorti.o cfunc.o $linpacklib $extralibs || export bstat="no"
fi
if [ x$tt == "xyes" ]; 
then
    if (([ "x$extralibs" != "x" ]) || ([ "x$linpacklib" != "x" ]));
    then echo "Linking mrcc with libraries: $linpacklib $extralibs"
    else echo "Linking mrcc"
    fi
    $fortran $compileropts $ompopt -o mrcc mrcc.o lambda.o pert.o mem.o xalloc.o combin.o signal.o $fls sacc.o dcommunicate3.o cfunc.o $linpacklib $extralibs || export bstat="no"
fi


The compiler options are set from another file build.mrcc.config. The program provides source code, but it's not open source. The license does not allow me to share the source code, so I am forced to provide snippets like this.

The output (stdout&stderr) from cygwin is here:(https://pastebin.com/NyVepHWv )

@jonnin, The program is supposed to run on WSL, but I was really hoping for a native compile, because in computational chemistry codes, speed is very crucial...in any case, I have to work with what I get in the end.
Last edited on
Pages: 12