none
CreateFileMapping problem RRS feed

  • Question

  • My code creates mapped memory that is used by several processes.  This works, but later when a Fortran program executes a RETURN statement, it crashes with an "Access violation reading location ..." error.

    There are some arguments in the call to CreateFileMapping that I do not understand.  Perhaps one of them would help.  Here is the code:

        int size = 6015401 + 128;

        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), derivfilename );

        double *deriv = static_cast<double*> (MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));

    Can anyone think of a security setting that I can change?  Perhaps in the nullptr argument?

    Friday, October 11, 2019 12:37 PM

Answers

  • After much trial and error, I narrowed down the problem.  I can compile the release version with optimization turned on for the Fortran code, but with optimization turned off for the C++ part.  Then everything works.

    Since the C++ is used only for the GUI where speed is not an issue, this is a satisfactory solution.  The actual cause is still a mystery, but it seems to point to the C++ not managing the call stack properly when optimized.  At this point, I don't care about those details.  What I have works.

    • Marked as answer by DonDilworth Tuesday, October 15, 2019 7:37 PM
    Tuesday, October 15, 2019 7:37 PM

All replies

  • This doesn't sound like a permissions related issue to me.  After all, aren't all the processes running under the same user account?

    Why do you believe that the access violation is a mapped memory problem and not something more mundane like attempting to read or write memory using a pointer containing an invalid address?

    Friday, October 11, 2019 12:59 PM
  • I'm just guessing at this point.  If I run my code in debug mode, it works perfectly.  In release mode, it crashes on the RETURN statement.  I'm guessing it's a permission problem simply because the error is an access violation reading .... 

    There is a permission parameter in the call to CreateFileMapping that is pretty opaque.  Can you suggest a better setting for that one?

    Perhaps the memory writing has clobbered the object code.  Any way to test for this?

    Friday, October 11, 2019 1:11 PM
  • Since all your processes are running under the same user account there is no need to adjust the security descriptor on the file mapping object.  Each process has identical permissions to use the file mapping.  That is why I don't believe that permissions are the problem.

    For C++ it is not uncommon for errors to manifest in release builds that were not seen in debug builds.

    A debug build will initialize variables with known values and pad dynamic allocations of buffers with additional memory that can help diagnose buffer overruns when using the facilities of the CRT debug library. But these extra allocations in a debug build can sometimes hide a buffer overrun that will appear in a release build.

    Is the process that has the access violation written in Fortran?

    Friday, October 11, 2019 1:36 PM
  • The project has both Fortran and C++, and they call each other in many places.

    It looks like something is being clobbered.  I have some clues that I'm tracking down.

    Friday, October 11, 2019 2:25 PM
  • One possible thing is that they are mapping to a different base address. This means that if you are writing pointers and using pointers written by another process, then they won't line up in memory.

    This is why MapViewOfFileEx was created so you could map to a known base address. This means that any pointers would make sense.

    The reason why this is not likely to be a permissions problem is that if all processes that access the file mapping are running under the same user account, the null for the security descriptor means that the file mapping will have the default security descriptor. This gives full access to the user account that created the object.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Friday, October 11, 2019 3:54 PM
  • One possible thing is that they are mapping to a different base address. This means that if you are writing pointers and using pointers written by another process, then they won't line up in memory.

    Absolutely.

    Based on the OP's initial post and some previous posts on the subject of  mapped memory by the OP I believe that the mapped memory is being used to store an array of double values.

    But you never know...


    • Edited by RLWA32 Friday, October 11, 2019 4:06 PM
    Friday, October 11, 2019 4:05 PM
  • The arrays store double-precision numbers.  The main process defines them:

        int size = 15001*412 + 128;

        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), derivfilename );

    Then the other processes access the memory with

        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, derivfilename );

    So I thought they would all read and write the same memory locations.  The numbers come back right, so I think that is true.

    Here's a wrinkle:  If I call the routine that calls the spawnl() from one place, it works.  If I call it from another subroutine, it crashes.  What does that tell you?  How can a return statement get corrupted?

    Friday, October 11, 2019 4:57 PM
  • So I thought they would all read and write the same memory locations.  The numbers come back right, so I think that is true.

    That would seem to indicate that mapped memory isn't the issue in and of itself.

    Here's a wrinkle:  If I call the routine that calls the spawnl() from one place, it works.  If I call it from another subroutine, it crashes.  What does that tell you?  How can a return statement get corrupted?

    This is a bit vague.  Are there any differences in the parameters passed to spawnl().  Maybe  _P_WAIT versus _P_NOWAIT.  That determines whether spawnl()'s return code is a process handle or an exit code.


    Friday, October 11, 2019 5:49 PM
  • The return address is placed on the stack. This means that it is possible to overwrite the return address.

    Since the return statement gets transformed into the assembly's ret instruction, this will read the address that it has to return to off of the stack and this will then remove the address off of the stack. If you somehow manage to add/remove a value on the stack without properly balancing the stack, or if you get a pointer to a stack variable and somehow manipulate the pointer to allow you to write to the location of the return address then you can get the ret instruction to read the wrong value off of the stack.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Friday, October 11, 2019 6:03 PM
  • There is only one spawnl().  I can get there by calling A | C or A | B | C.  There are no arguments in the call to C.  The first works; the second crashes when C tries to return to B.

        imsg.Format( "%d", i );        // is new core number
        jmsg.Format( "%d", j );        // is task number
        kmsg.Format( "%d", k );        // is network flag
        tmsg.Format( "%d", frametop );    // data set by core 0
        lmsg.Format( "%d", frameleft );
            
        l = spawnl( _P_NOWAIT, path, "SYNOPSYS200v15.exe", imsg, jmsg, kmsg, tmsg, lmsg, NULL );
        if ( l < 0 ) {
            errno_t err = 0;

            _get_errno( &err );
            switch (err) {

    Friday, October 11, 2019 6:03 PM
  • Very cogent reply, thank you.

    I tried setting one of the run-time options on the release version, to check the stack frame.  Now the program does not crash, and there is no error message.  But execution is about half the usual speed.

    Looks like just checking the stack moves things around and avoids the problem.  I hate to lose the speed, though.

    Friday, October 11, 2019 7:06 PM
  • I tried setting one of the run-time options on the release version, to check the stack frame.  Now the program does not crash, and there is no error message.  But execution is about half the usual speed.

    Looks like just checking the stack moves things around and avoids the problem.  I hate to lose the speed, though.

    This doesn't sound like a good solution to me.  All it may mean is that a different location in memory (unrelated to return) is getting corrupted.  This may or may not manifest as some other problem later on.

    Friday, October 11, 2019 7:11 PM
  • I'm not pleased with it either, but it works.  Can you suggest anything else?  I've checked the arrays I create and share with the other processes, and all looks correct.  Is there any way to just check the return address for a single subroutine?  To see if it gets corrupted?

    The debug version never crashes and shows no errors.  That is supposed to check array boundaries.

    Friday, October 11, 2019 8:12 PM
  • The array bounds check is limited. Depending on what you are checking, it may not find it.

    The amount of space that the compiler adds around a variabe is limited. For example:

    void fun()
    {
    	int arr[10]{};
    
    	*(static_cast<int *>(arr) - 2) = -2;
    }
    
    int main()
    {
    	fun();
    	return 0;
    }

    this is only 8 bytes before the start of the array and the bounds checking won't see this. The same is true after the array. If you access arr[11], Visual Studio 2019 at the very least doesn't see this.

    However, this is what the SDL checks are there for. If you have an array type with a size, the compiler will output:

    1>C:\Users\Darran\source\repos\meh\meh\main.cpp(7): error C4789: buffer 'arr' of size 40 bytes will be overrun; 4 bytes will be written starting at offset 44

    for Visual Studio 2019 with /SDL enabled.

    There is no checks if the array that you are working with is on the heap.

    This is why C style arrays are discouraged and using C++'s std::array or std::vector is encouraged if you can. This is also why the C++ committee voted in std::span, to give a C++ class view around arrays. This was added to C++20 though and isn't in Visual C++ yet. The one additional bonus to the C++ classes is that they will assert in debug mode and just silently ignore the write in release mode.

    Anyway, if you want to verify if the return address has been corrupted then you will probably have to do something like:

    #include <vector>
    #include <intrin.h>
    
    #pragma intrinsic(_ReturnAddress)
    
    std::vector<void *> addresses;
    
    void fun()
    {
    	addresses.push_back(_ReturnAddress());
    
    
    	if (_ReturnAddress() != addresses.back())
    	{
    		//return address became corrupted
    		terminate();
    	}
    	addresses.pop_back();
    }
    
    int main()
    {
    	addresses.push_back(_ReturnAddress());
    
    	fun();
    
    	if (_ReturnAddress() != addresses.back())
    	{
    		//return address became corrupted
    		terminate();
    	}
    	addresses.pop_back();
    	return 0;
    }

    The _ReturnAddress function is a compiler intrinsic, and the reason why I don't use the stack is because the stack is probably corrupt if the return address is corrupt. This isn't the greatest way of doing this, but it is short and easy enough.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Friday, October 11, 2019 11:06 PM
    Friday, October 11, 2019 10:55 PM
  • I think this is getting close.  But the return that crashes is in a Fortran program, not C++.  If I use your suggested code and put it in a C++ program that gets called, I get the return address from that program, not the Fortran that called it.

    Still, we're close.  How can I monitor the return address from the Fortran?  If I can do that, I can watch and see where it gets clobbered.

    The stack checking option that works seems to disable some optimizations.  That could be a clue also.

    Saturday, October 12, 2019 5:24 PM
  • Here's a new twist:  If I disable optimization in the Property Pages for Fortran, the program does not crash.  I suspect my trick of making several Fortran instances share memory is not well handled by the optimizer.  I turned off the option for stack frame checking.

    But I lose execution speed.  Any suggestions?

    Saturday, October 12, 2019 5:52 PM
  • I don't know fortran at all so I can't say much. Because I don't know fortran I have no idea if it can monitor its own stack.

    All I know for sure is that monitoring the stack from inside the application itself would require a lot of work since there is no way to know if another thread has called a new function. If you wanted to keep an eye on this then you would have to do extensive amounts of work.

    This means that without a way for the fortran code to monitor its own stack then the only option that I know you have is the debugger itself. When the debugger has paused execution then it will show the call stack for the thread. If you step through the code then you can watch to see if the call stack changes.

    Unfortunately there are times when bugs are this annoying to find.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Saturday, October 12, 2019 6:24 PM
  • I don't know Fortran so I can't help you there.  Perhaps you should consider raising this issue in a forum that supports the Fortran compiler you are using.
    Saturday, October 12, 2019 7:13 PM
  • Mixed language programming is like that.  I have some new data, though.

    Fortran routine SYNO calls a C++ program:

        call preparederivmap( NPAS )      ! THIS IS SO OTHER CORES USE THE SAME DERIVATIVE ARRAY; CALLS SYNORUN

    C++ calls a Fortran program:

        SYNORUN( deriv, delq, scdr, &npas );    // returns only when all passes are done

    At this point, the call stack looks good.  Then I return from the C++, which goes back to SYNO.

    But instantly, the call stack goes away, and I see only the address of the current line in SYNO, not the routine that called it.  So when SYNO tries to return to its caller, it crashes.

    Does that tell you anything?

    Sunday, October 13, 2019 4:19 PM
  • All this says is that the return from the C/C++ function to the fortran function is messing up the stack. Beyond that though, not much. Without an understanding of the code, we can only speculate.

    The three possibilities are as follows.

    1) You are passing a pointer to a variable in to the fortran function. If the fortran function has different type information for the variable then this could overwrite the return address.

    2) The C/C++ function is somehow writing to the stack. This takes a lot of work though since it often takes over 272 bytes written to a string to overwrite the return adddress. Because stack corruption is a very common security vulnerability, C/C++ compilers have become hardened to this so it would be tough to do this. If you call the C/C++ function from C/C++ with the parameters that fortan passes in then you can rule this out.

    3) There is a problem with fortran and the C/C++ ABI.

    While you can do some testing, to be honest I would say this is a problem with the fortran portion of your application. Stack corruption is common when there are ABI problems involved.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Sunday, October 13, 2019 7:39 PM
  • I do indeed pass a pointer to the fortran.  Three of them.  So this is a good clue.

    Here's another:  If I compile in release mode with optimization turned off, the problem goes away, but execution is slower.  If I turn on any optimization of the fortran code, it's crash time.  I declared all the arrays pointed at as VOLATILE, so they won't be optimized out.  Still crashes.

    So it looks like the culprit is in the optimization done by the compiler.  Is there any way to turn off optimization for only a given subroutine?  That would be a good test.

    Monday, October 14, 2019 12:38 PM
  • For Visual C++, sure, there is #pragma optimize, but this obviously only affects the C++ compiler.

    For fortran, no idea. This is determined by whatever compiler you use to compile the fortran code so you would have to check the documentation.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Monday, October 14, 2019 12:53 PM
  • Wrong calling convention and/or signature while importing the foreign functions ?
    Monday, October 14, 2019 1:05 PM
  • There is a fortran directive to turn off optimization, and I tried it in a couple of places.  No luck.  It looks like the only practical solution is just to compile without optimization.  At least that works.
    • Marked as answer by DonDilworth Monday, October 14, 2019 1:49 PM
    • Unmarked as answer by DonDilworth Monday, October 14, 2019 11:02 PM
    Monday, October 14, 2019 1:49 PM
  • It certainly looks like I have corrupted the stack somehow.  Here's a thought:

    When I create the mapped file, I give it a large size:

        int size = 15001*412 + 128;

        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), derivfilename );
        if (hMapping == NULL ) {
            panic( "Cannot open mapping file" );
        }

        double *deriv = static_cast<double*> (MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !deriv ) {
            panic( "Cannot do MapView" );
        }
        pderiv = deriv;

    The arguments for CreateFileMapping specify two DWORDS for the file size, so my question is, can I get that huge file size into just the low-order word?  If not, how do I break it up into two words?  I've never done that.  If that's my problem, it is easily fixed.

    HANDLE CreateFileMappingA( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName );

    Monday, October 14, 2019 11:07 PM
  • The thing is, CreateFileMapping and MapViewOfFile would have nothing to do with your stack corruption problem. CreateFileMapping doesn't really allocate any memory beyond what it needs to create the file mapping object.

    Basically the file mapping object is basically a translation layer between memory access and file access. It translates *ptr = value to WriteFile calls and var = *ptr to ReadFile calls, nothing more.

    MapViewOfFile is what actually puts the file mapping into memory, and this doesn't touch the stack beyond what is needed to call the function. This maps the file to untouched pages in the virtual address space, so it doesn't even allocate memory for that. When there is no file handle, this is basically a view into the page file.

    There is other which is against the MapViewOfFile call somehow corrupting the stack.

    #include <Windows.h>
    #include <cstdio>
    #include <intrin.h>
    
    int main()
    {	
    	constexpr int size = 15001 * 412 + 128;
    
    	HANDLE fm = CreateFileMappingW(nullptr, nullptr, PAGE_READWRITE, 0, size * sizeof(double), L"Object Name");
    	void * base = MapViewOfFile(fm, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    
    	wprintf(L"Mapping base address %p\n", base);
    	wprintf(L"Address of return address %p\n", _AddressOfReturnAddress());
    
    	return 0;
    }

    This example always output data like:

    Mapping base address 01D30000
    Address of return address 00E5F914

    Mapping base address 000001203CC80000
    Address of return address 0000000495DFF6D8

    Notice how the return address on the stack is at a lower address than the address that the file mapping is placed at. I have run this a lot of times so far and the address where the stack is at has always been at a lower address than the address that the file mapping gets mapped in at.

    This means that you would have to somehow take the address that the file mapping starts at and write to at least 15 Megabytes before the start of the file mapping for the 32 bit version. For the 64 bit version you have to manage to miss by around 1.2 Terrabytes (I'm not kidding with this).

    To put it simply, since there is so much distance between the stack and the file mapping then you must do something extremely wrong in order to miss the file mapping and hit the stack.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    • Edited by Darran Rowe Tuesday, October 15, 2019 12:56 AM
    Tuesday, October 15, 2019 12:45 AM
  • You are allocating memory for doubles. A double is 8 bytes.

    15001 * 412 + 128 = 6,180,540

    6,180,540 * 8 = 49,444,320

    An int can store values from -2,147,483,648 to 2,147,483,647 and a DWORD (unsigned long) can store values from 0 to 4,294,967,295

    So the number of bytes that you are requesting will safely be held by a single DWORD.

    Tuesday, October 15, 2019 12:52 AM
  • The good new is, my calls to CreateFileMapping are not corrupting the stack.

    The bad new is, what else could be doing that?  I've checked the code many times, and all use of the file is within bounds.

    Two questions:  Is there any way to monitor the stack from within a fortran program?  (All reads and writes are done there)?  If I knew where it is corrupted, that would help.

    And what is the difference between CreateFileMapping and CreateFileMappingW, as referenced by Darran?  Does that matter?

    Tuesday, October 15, 2019 1:32 PM
  • And what is the difference between CreateFileMapping and CreateFileMappingW, as referenced by Darran?  Does that matter?

    There are two flavors depending on which character set your project is building with -- CreateFileMappingA and CreateFileMappingW.

    The Windows headers will resolve CreateFileMapping to one or the other, depending on whether you are building for MBCS or UNICODE. 

    Short answer -- it doesn't matter.

    Tuesday, October 15, 2019 1:37 PM
  • Is there any way to monitor the stack from within a fortran program? (All reads and writes are done there)? If I knew where it is corrupted, that would help.

    The Visual Studio debugger is language agnostic. If you switch to the disassembly view then you can step through instruction by instruction. The debugger updates all of this information on each break into the debugger, this means that every time you press F10/F11 then the information will be refreshed.

    This means if you are stepping through assembly, instruction by instruction, then you will see the exact instruction that causes this corruption. If you have debug symbols loaded, assuming the fortran compiler can generate .pdb symbols, then you can tell what function this goes wrong in.

    Since I have never touched fortran before this is the only advice I can give in tracking down the problem using the tools that I know. This is more than likely a binary compatibility problem and this means that you are going to look at the generated assembly in order to figure out what's going wrong. I couldn't know if fortran is capable of doing this since I don't know the language and I wouldn't want to give bad answers based on random internet searches.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Tuesday, October 15, 2019 4:14 PM
  • After much trial and error, I narrowed down the problem.  I can compile the release version with optimization turned on for the Fortran code, but with optimization turned off for the C++ part.  Then everything works.

    Since the C++ is used only for the GUI where speed is not an issue, this is a satisfactory solution.  The actual cause is still a mystery, but it seems to point to the C++ not managing the call stack properly when optimized.  At this point, I don't care about those details.  What I have works.

    • Marked as answer by DonDilworth Tuesday, October 15, 2019 7:37 PM
    Tuesday, October 15, 2019 7:37 PM
  • The actual cause is still a mystery, but it seems to point to the C++ not managing the call stack properly when optimized.

    Have you confirmed this? It is easy enough to just go to the C++ function in the debugger and step through this. If the base pointer and stack pointer registers have the same value at the very first instruction as it does at the end of the function then it balances the stack.

    This is highly likely a fortran problem. The fortran function has to set up the stack in order to call the C++ function. For everything but __stdcall, the fortran function is also responsible for cleaning up the stack after the C++ function returns. If you tell the fortran compiler that the function is C calling convention but the function is __stdcall, or tell fortran that the function is an __stdcall function but the function is a C calling convention function then this is exactly the type of behaviour that you would expect to see.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Tuesday, October 15, 2019 8:51 PM
  • For me, this is not so easy.  The release version has no debug information.  Shall I learn how to look at the pointers, even though the problem is solved?  There is a cost/benefit issue here.

    Since the fortran portion works both optimized and not, and the answers are all correct, I suspect that all the calling conventions are correct too.

    The program only works if the C++ is not optimized.  That points to an optimizer issue.  Thank you for your suggestion.

    Wednesday, October 16, 2019 12:38 PM
  • Then the easy thing to do would be to provide the C++ code which is aparently causing problems. If this is a compiler bug as you are implying then ignoring it is a disservice to Microsoft and a disservice to other developers who could run into this problem.

    If you are unwilling to determine if this is a bug then allow someone else to do this and report it if needed.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Wednesday, October 16, 2019 2:50 PM
  • You are absolutely correct.  Here is the C++ code that seems to be the culprit.  The first is called from fortran, creates three mapped files, and calls a fortran program SYNORUN, which spawns several processes and waits for them to finish.  Those call mymap2() to access the mapped files.  Then they call a fortran program DODLOOP, sending the addresses of the files.  Fortran then writes to those files.  When all processes are finished, SYNORUN crunches the data that have been written and terminates the other processes.

    If you can spot any weaknesses in this code, I will be grateful.  The crash occurs when SYNORUN returns and preparederivmap tries to return.  Then the stack is corrupted.

    double* pderiv;
    double* pdelq;
    double* pscdr;

    void preparederivmap( int &npas )    // create mapping of data for other cores
    {
        int size = 15001*412 + 128;

        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), derivfilename );
        if (hMapping == NULL ) {
            panic( "Cannot open mapping file" );
        }

        double *deriv = static_cast<double*> (MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !deriv ) {
            panic( "Cannot do MapView" );
        }
        pderiv = deriv;

        size = 401;
        TCHAR delqfilename[]= TEXT("SYNOPSYS_DELQ_DATA");

        hMapping2 = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), delqfilename );
        if (hMapping == NULL ) {
            panic( "Cannot open mapping file" );
        }

        double *delq = static_cast<double*> (MapViewOfFile(hMapping2, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !delq ) {
            panic( "Cannot do MapView" );
        }
        pdelq = delq;

        TCHAR scdrfilename[]= TEXT("SYNOPSYS_SCDR_DATA");

        hMapping3 = CreateFileMapping( NULL ,nullptr, PAGE_READWRITE, 0, (size*sizeof(double)), scdrfilename );
        if (hMapping == NULL ) {
            panic( "Cannot open mapping file" );
        }

        double *scdr = static_cast<double*> (MapViewOfFile(hMapping3, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !scdr ) {
            panic( "Cannot do MapView" );
        }
        pscdr = scdr;

        SYNORUN( deriv, delq, scdr, &npas );    // returns only when all passes are done
        return;
    }

    /////////////////////////////////////////////////////////////////////////////

    void mymap2( )    // read mapping of data for other cores
    {
        TCHAR derivfilename[]= TEXT("SYNOPSYS_DERIV_DATA");

        hMapping = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, derivfilename );
        if (hMapping == NULL ) {
            panic( "Cannot reopen mapping file" );
        }

        double *deriv = static_cast<double*> (MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !deriv ) {
            panic( "Cannot redo MapView" );
        }

        TCHAR delqfilename[]= TEXT("SYNOPSYS_DELQ_DATA");

        hMapping2 = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, delqfilename );
        if (hMapping == NULL ) {
            panic( "Cannot reopen mapping file" );
        }

        double *delq = static_cast<double*> (MapViewOfFile(hMapping2, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !delq ) {
            panic( "Cannot redo MapView" );
        }

        TCHAR dscdrfilename[]= TEXT("SYNOPSYS_SCDR_DATA");

        hMapping3 = OpenFileMapping( FILE_MAP_ALL_ACCESS, FALSE, delqfilename );
        if (hMapping == NULL ) {
            panic( "Cannot reopen mapping file" );
        }

        double *scdr = static_cast<double*> (MapViewOfFile(hMapping3, FILE_MAP_ALL_ACCESS, 0, 0, 0 ));
        if ( !scdr ) {
            panic( "Cannot redo MapView" );
        }

        showcorebox();

        DODLOOP( deriv, delq, scdr );    // gets derivs.  Returns after all iterations are done, this core.

        killcorebox();

    }


    • Edited by DonDilworth Wednesday, October 16, 2019 3:12 PM
    Wednesday, October 16, 2019 3:09 PM
  • What is the calling convention for fortran? are the fortran functions declared correctly from C++ point of view?

    -- pa

    Thursday, October 17, 2019 2:36 AM
  • Well, besides your code opening the same file mapping twice:

    hMapping2 = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, delqfilename);
    /*
    ...
    */
    hMapping3 = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, delqfilename);

    The code generated under /O2 looks fine. Putting in some filler code to get it to build, this runs and exits normally. I have tried using a couple of different versions of the compiler (last available version of Visual Studio 2015, 2017 and current 2019) but there is no sign of any bugs in the stack handling.

    This means one of two things:

    1) You are using a version that I didn't test where there is a bug, but this has since been fixed.

    2) The problem isn't with the C++ code but is only manifesting itself where it is.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.

    Thursday, October 17, 2019 1:00 PM
  • Perhaps the posted code isn't what is actually being executed. 

    Another glitch is that although the posted code uses hMapping, hMapping2 and hMapping3 all of the error checking after OpenFileMapping calls checks the hMapping handle.

    Thursday, October 17, 2019 1:06 PM
  • I thank the respondents for pointing out these careless errors.  Strangely enough, they have no effect on the calculations, which are correct, or on the crash if I compile the C++ with optimization.

    As for the calling convention, here are the prototypes for calling fortran from C++:

    extern "C" void DODLOOP(double* deriv, double* delq, double* scdr);    // loop over variables, get derivatives
    extern "C" void SYNORUN(double* deriv, double* delq, double* scdr, int* npass );    // starts process 0

    And here is the fortran code that calls the C++:

        SUBROUTINE SYNO
          IMPLICIT DOUBLE PRECISION (A-H,O-Z)
        SAVE        ! NEED THIS SO RETURN IS REMEMBERED
        INCLUDE 'LENS200.INC'
        INCLUDE 'OPTIM.INC'
        COMMON/A/KIN,KOUT,I1DUM,I2DUM,JDATSW(110),ISFLAGS(300),SFLAGS(300),JCONF
        COMMON/DERIVCOM/DERIV(15001*412+128)
        DIMENSION SCDR(MXVAR)
          VOLATILE DERIV,DELQM,SCDRM,NPAS
          
          INTERFACE TO SUBROUTINE preparederivmap[C,ALIAS:'_preparederivmap'] ( NPASS )
          INTEGER NPASS [REFERENCE]
          END
          
          NPAS = 0
          
          IF (JDATSW(99) .EQ. 2 .OR. ISFLAGS(206) .GT. 0 .OR. ISFLAGS(171) .EQ. 0  ! OTHER CORES COMING IN HAVE TO COME HERE, OR CORE 0
         $ .OR. ISFLAGS(212) .EQ. 1 .OR. ISFLAGS(171) .LT. 2) THEN   ! OTHER CORES COMING IN HAVE TO COME HERE, OR CORE 0
              CALL SYNORUN( DERIV, DELQ, SCDR, NPAS)   ! STRAIGHT THROUGH, USES CURRENT CORE ONLY
              RETURN
        ENDIF
     
          JDATSW(82) = 2            ! SWITCH 82 NOT SUPPORTED BY SYNO | spawn
          ISFLAGS(293) = ISFLAGS(171) - 1
          IF (ISFLAGS(293) .GT. NVAR) ISFLAGS(293) = NVAR ! RUN THIS MANY CORES ONLY
        call preparederivmap( NPAS )      ! THIS IS SO OTHER CORES USE THE SAME DERIVATIVE ARRAY; CALLS SYNORUN
          
        END



    Friday, October 18, 2019 1:15 PM
  • As for the calling convention, here are the prototypes for calling fortran from C++:
    extern "C" void DODLOOP(double* deriv, double* delq, double* scdr);    // loop over variables, get derivatives
    extern "C" void SYNORUN(double* deriv, double* delq, double* scdr, int* npass );    // starts process 0

    Does your FORTRAN compiler think they're "C" calling convention though?
    Ditto for the vice-versa case.

    https://devblogs.microsoft.com/oldnewthing/20101222-00/?p=11943

    Dave

    Friday, October 18, 2019 3:27 PM
  • As a superannuated fortran programmer, I can only guess.  Fortran calls C++ and C++ calls Fortran all over the place, and the numbers and arrays that show up have all the right values.  I'm guessing that if the calling convention were screwed up, disasters would have occurred long ago.  So, yes, I think the calling conventions are all correct.

    That said, we still have two mysteries:  Why compiling C++ with optimization makes the call stack of the release version collapse upon return from preparederivmap, and why compiling SYNO without a SAVE directive makes the debug version do the same thing.

    I'm open to suggestions, but personally I think this is a case of the optimizer making too many changes at some point.  And I'm not convinced that Windows 10 is perfect either.  If it were, they would not be updating it every few days.  That's buck-passing for sure, but what else is there at this point?

    Friday, October 18, 2019 5:51 PM
  • Shouldn't the first CreateFileMapping argument be INVALID_HANDLE_VALUE ?

    Friday, October 18, 2019 10:09 PM
  • As a superannuated fortran programmer, I can only guess.

    Guessing is never good - why not check your compiler documentation and be sure?

    Fortran calls C++ and C++ calls Fortran all over the place, and the numbers and arrays that show up have all the right values.  I'm guessing that if the calling convention were screwed up, disasters would have occurred long ago.

    Some may be defined correctly, others not.

    Dave

    Friday, October 18, 2019 11:54 PM
  • Shouldn't the first CreateFileMapping argument be INVALID_HANDLE_VALUE ?

    According to the documentation, yes.

    Interestingly, the function succeeds if NULL is passed instead.

    Saturday, October 19, 2019 10:12 AM
  • That was not helpful.  You assume I have not read the documentation?  Tried to make sense of it?  Found it so confusing I had to resort to guessing?  Please understand that not everyone is at the same level as you experts.

    Go ahead and try to find a tutorial that explains all you need to know about mixed-language programming.  Bits and pieces here and there, that's all.  From then on it's guesswork.

    Saturday, October 19, 2019 12:47 PM
  • See if Dr.Memory can help.

    https://drmemory.org/

    Saturday, October 19, 2019 2:52 PM