简体   繁体   中英

ZeroMQ context singleton, provided in a DLL, crashes when program exits (VS2010 win7 x64 zmq 4.0x)

This is the singleton

#pragma once

class ContextManager {
public:
    static ContextManager& Instance() {
        static ContextManager instance;
        return instance;
    }
    zmq::context_t& GetContext() { return ctx_;}
private:
    zmq::context_t ctx_;
    ~ContextManager() {}
};

I have a DLL with some useful Network utilities, built on ZeroMQ and using this singleton for not having to pass context around.

I link this DLL to an EXE which runs a test-suite. This test suite works, sending and receiving some messages. When the program exits, the ContextManager destructor crashes saying " Assertion failed: Successful WSASTARTUP not yet performed (......\\src\\signaler .cpp:137) "

More details:

  • The application is single-threaded.
  • If I just call the Instance.GetContext() method from the .EXE and return (no test running, no more calls to the DLL interface), then it fails too.
  • If I define this singleton before the main (thus, inside the exe without using the object from the DLL) then it works .
  • WSastartup is called just once and it works.

I do not want to expose any implementation details to the DLL clients, so I would like to have this singleton inside the DLL. How could achieve this?

The problem is that WinSock, which is used by ZMQ, requires a call to WSAStartup() before use. If you then call WSAShutdown() and use ZMQ, it looks as if WSAStartup() had never been called, hence the failed assertion. On a more abstract level, the timespan between WSAStartup() and WSAShutdown() must completely contain the lifetime of the ZMQ context.

Function-level statics in C++ are created on demand but destroyed (I believe in unspecified order) after main() returns. You don't show the call to WSAStartup(), but I guess it is somewhere inside main(). Similarly, the call to WSAShutdown() is before the end of main, but that would still put it before the destruction of function-static objects, hence the problems you are seeing.

Two possible fixes:

  • Allocate the context using new and never delete it. The only time you would delete it is on program shutdown, shortly before the OS itself would reclaim all the resources used by the program. This is a simple and pragmatic fix.
  • A little more complicated would be to bind the calls to WSAStartup()/WSAShutdown() to the ctor/dtor of the singleton. In the ctor, start WinSock and then create the context. In the destructor, destroy the context and then release WinSock.

You could also create two functions similar to WSAStartup() and WSAShutdown() for your DLL, but that's inconvenient and ugly. Also, I would at least consider not using singletons unless absolutely necessary. Forcing a certain use of your code on the user is a nuisance, but that's just my personal opinion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM