I have an ancient C++ application originally built in Visual C++ 6.0, that uses a very complex shared-memory DLL to share data among about 8 EXEs and DLLs that all have a pool of values that could be replaced by one or two dictionaries with Strings for the keys, and records for the values. The application is multi-threaded and multi-process. There are three primary executables reading and writing into the shared memory area, and several of the executables have 3 or more threads that read/write or "queue" information into this pooled memory area. About a few hundred places, Structured Exception Handling (SEH) of the __try and __except is used to filter exceptions, and to try to handle Access Violations by resizing the shared memory, which are in segments managed by a class called CGMMF which means growable memory mapped file.

The most salient details are shown here because I cannot find any cohesive source of documentation on the technique in use, or it's safety and suitability. Experimentally I have found that this library didn't work very well on a single core system in 1998, it works somewhat on a single-core virtual machine running windows XP, and that it doesn't work at all on modern 2+ ghz multi-core Windows 7 64-bit systems in 2013. I'm trying to repair it or replace it.

#define ResAddrSpace(pvAddress, dwSize)  \
   (m_hFileMapRes = CreateFileMapping(HFILE_PAGEFILE, &m_SecAttr,             \
      PAGE_READWRITE| SEC_RESERVE, 0, dwSize, m_szRegionName),                    \
   (m_hFileMapRes == NULL) ? NULL :                                     \
      MapViewOfFileEx(m_hFileMapRes, FILE_MAP_ALL_ACCESS, 0, 0, dwSize, 0))


void CGmmf::Create(void)
{
    DWORD dwMaxRgnSize;
    if (Gsinf.dwAllocationGranularity == 0) 
    {
        GetSystemInfo(&Gsinf);
    }
    m_dwFileSizeMax = RoundUp(m_dwFileSizeMax, Gsinf.dwAllocationGranularity);
    m_dwFileGrowInc = RoundUp(m_dwFileGrowInc, Gsinf.dwAllocationGranularity);
    dwMaxRgnSize = m_dwFileSizeMax + m_dwOverrunBuf;
    m_pbFile = (PBYTE)ResAddrSpace(NULL, dwMaxRgnSize);
        Adjust(m_dwFileSizeNow);
}

void CGmmf::Adjust(IN DWORD dwDiskFileNow) 
{
    int nThreadPriority;

    __try 
    {
        //
        // Boost our thread's priority so that another thread is 
        // less likely to use the same address space while 
        //  we're changing it.
        //
        nThreadPriority = GetThreadPriority(GetCurrentThread());
        SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

        //
        // Restore the contents with the properly adjusted lengths
        //
        Construct(dwDiskFileNow);
    }
    __finally 
    {       
        //
        // Make sure that we always restore our priority class and thread 
        // priority so that we do not continue to adversely affect other 
        // threads in the system.
        //

        SetThreadPriority(GetCurrentThread(), nThreadPriority);
    }
}


void CGmmf::Construct(IN DWORD dwDiskFileNow) 
{
    DWORD dwDiskFileNew = RoundUp(dwDiskFileNow, m_dwFileGrowInc),
          dwStatus = ERROR_SUCCESS;

    PBYTE pbTemp;

    if (dwDiskFileNew > 0) 
    {
        //
        // Grow the MMF by creating a new file-mapping object.
        //
            // use VirtualAlloc() here to commit
        // the requested memory: VirtualAlloc will not fail
        // even if the memory block is already committed:

        pbTemp = (PBYTE)VirtualAlloc(m_pbFile,dwDiskFileNew,MEM_COMMIT,PAGE_READWRITE);

        if(NULL == pbTemp)
        {
            LogError(GetLastError(), MEM_CREATE_MMF, m_szRegionName);

            // 
            //  File-mapping could not be created, the disk is 
            //  probably full.
            //
            RaiseException(EXCEPTION_GMMF_DISKFULL, 
                           EXCEPTION_NONCONTINUABLE, 
                           0, 
                           NULL);
        }

        // 
        //  Check to see if our region has been corrupted 
        //  by another thread.
        //
        if (pbTemp != m_pbFile)
        {
            RaiseException(EXCEPTION_GMMF_CORRUPTEDRGN, 
                           EXCEPTION_NONCONTINUABLE, 
                           0, 
                           NULL);
        }
    }
}

So far my options for replacing it include attempting to replace all the shared memory with DCOM (out of process COM) and COM (in process COM) as appropriate to the places where the memory mapped files, and to guard against concurrency issues by hand, using synchronization/mutex/criticalsection or other threadsafe constructs as appropriate.

I want to know if there is already some thread-safe memory-dictionary type I could replace all of this with. Even in the above snippet which is less than 1% of the code of this ancient shared-memory-library-for-visual-C++-6, there are things that make me shudder. For example, raising thread priority as a strategy for avoiding deadlocks, race conditions and general corruption. Maybe that used to make this code stop crashing quite so much on an 80486 CPU at 33 mhz. Shudder.

I have the code building and running in Visual C++ 6.0 and also a branch of it runs in Visual C++ 2008, and I could probably get it going in Visual C++ 2010. What could I use that would give me dictionary semantics, shared memory across processes, and is stable and reliable?

Update By "dictionary" I mean the dictionary datatype as known in Python, which is also called a "key/value store" in some places, and in others (like in the C++ standard library), it's known as std::map. Boost documentation that discusses this is here.

有帮助吗?

解决方案

It sounds like you should take a look at Boost Interprocess. You can use it to have std::map-like objects in a shared memory and a lot more. Its been years since I used it last time, so cannot go into much details, but the library documentation is good and has tons of examples, it should get you going in 30 minutes.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top