11

I was wondering how to share some memory between different program modules - lets say, I have a main application (exe), and then some module (dll). They both link to the same static library. This static library will have some manager, that provides various services. What I would like to achieve, is to have this manager shared between all application modules, and to do this transparently during the library initialization. Between processes I could use shared memory, but I want this to be shared in the current process only. Could you think of some cross-platform way to do this? Possibly using boost libraries, if they provide some facilities to do this.

Only solution I can think of right now, is to use shared library of the respective OS, that all other modules will link to at runtime, and have the manager saved there.

EDIT: To clarify what I actually need:

  1. I need to find out, if the shared manager was already created (the answers below already provided some ways to do that)
  2. Get the pointer to the manager, if it exists, or Set the pointer somewhere to the newly created manager object.
Jan Holecek
  • 2,131
  • 1
  • 16
  • 26
  • Given that this is a single process, how is this different from a normal singleton pattern? – sdg Jan 06 '11 at 14:56
  • It is in fact a singleton, problem is how to implement it, so it will be shared between different shared libraries. – Jan Holecek Jan 06 '11 at 15:05
  • Inside the same process, any module can access external global variables without any restriction. – 9dan Jan 06 '11 at 15:09
  • Even if they are defined in the static libraries, that the shared ones link to? I would imagine this may work, if I use the dynamic runtime libraries, but the fact is, I need this to work even if I use the static CRT. – Jan Holecek Jan 06 '11 at 15:14

4 Answers4

13

I think you're going to need assistance from a shared library to do this in any portable fashion. It doesn't necessarily need to know anything about the objects being shared between modules, it just needs to provide some globally-accessible mapping from a key (probably a string) to a pointer.

However, if you're willing to call OS APIs, this is feasible, and I think you may only need two implementations of the OS-specific part (one for Windows DLLs and GetProcAddress, one for OSes which use dlopen).

As each module loads, it walks the list of previously loaded modules looking for any that export a specially-named function. If it finds one (any, doesn't matter which, because the invariant is that all fully-loaded modules are aware of the common object), it gets the address of the common object from the previously loaded module, then increments the reference count. If it's unable to find any, it allocates new data and initializes the reference count. During module unload, it decrements the reference count and frees the common object if the reference count reached zero.

Of course it's necessary to use the OS allocator for the common object, because although unlikely, it's possible that it is deallocated from a different library from the one which first loaded it. This also implies that the common object cannot contain any virtual functions or any other sort of pointer to segments of the different modules. All its resources must by dynamically allocated using the OS process-wide allocator. This is probably less of a burden on systems where libc++ is a shared library, but you said you're statically linking the CRT.

Functions needed in Win32 would include EnumProcessModules, GetProcAddress, HeapAlloc, and HeapFree, GetProcessHeap and GetCurrentProcess.

Everything considered, I think I would stick to putting the common object in its own shared library, which leverages the loader's data structures to find it. Otherwise you're re-inventing the loader. This will work even when the CRT is statically linked into several modules, but I think you're setting yourself up for ODR violations. Be really particular about keeping the common data POD.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • Well that's a really creative solution:-) I wonder, if the application executable links to some shared libraries (automatically at startup), and at the same time it has some global variables defined, is the order of loading the libraries/initializing global variables defined by the standard, or is it implementation defined? If the global data gets initialized first, then we can perform the initialization in the main executable, and do not worry about the different mem allocators in case of static CRT linking. – Jan Holecek Jan 06 '11 at 18:54
  • 1
    @John: Oh, are you guaranteeing that these variables exist in the main executable? I thought you were designing for the general case: trying to build a library that works whether the application uses the same helper object or not. Anyway, libraries are not addressed by the C++ standard AT ALL, so specifically the order of loading is not defined by the standard. However, on most platforms libraries are handled by the OS loader before any constructors run. Of course with delay-load or explicit loading the libraries don't load during startup but when that code executes. – Ben Voigt Jan 06 '11 at 19:38
  • Yes, the library is supposed to be generic, so it was just a thought. – Jan Holecek Jan 06 '11 at 19:44
1

For use from the current process only, you don't need to devise any special function or structure.

You could do it even without any function but it is more safe and cross platform friendly to define set of functions providing access to the shared data. And these functions could be implemented by the common static library.

I think, only concern of this setup is that: "Who will own the data?". There must exist one and only one owner of the shared data.

With these basic idea, we could sketch the API like this:

IsSharedDataExist     // check whether of not shared data exist

CreateSharedData      // create (possibly dynamically) shared data

DestroySharedData     // destroy shared data

... various data access API ...

Or C++ class with the Singleton pattern will be appropriate.


UPDATE

I was confused. Real problem can be defined as "How to implement a Singleton class in a static library that will be linked with multiple dynamic loading library (will be used in the same process) in platform independent way".

I think, basic idea is not much different but make sure the singleton is the really single is the additional problem of this setup.

For this purpose, you could employ Boost.Interprocess.

#include <boost/config.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
...
boost::interprocess::named_mutex* singleton_check = 0;

// in the Create function of the singleton
try {
    singleton_check = new boost::interprocess::named_mutex(boost::interprocess::create_only, "name_of_the_mutex" );
    // if no exception throw, this is the first time execution
}
catch (...)
{
}

Freeing the named_mutex is as simple as delete singleton_check.


UPDATE#2

Another suggestion.

I think, we should not place shared data in the common static library. If we can not ensure globally unique data, it is not only tricky platform dependent implementation problems but also waste of memory and global resources.

If you prefer static library implementation you should make two static libraries. One for the server/creator of the shared data, one for users of that shared data. Server library define and provide access to the Singleton. Client library provide various data access method.

This is effectively same as the Singleton implementation without static libraries.

9dan
  • 4,222
  • 2
  • 29
  • 44
  • The way I was going to approach this, was to check for the named memory - if it already existed, the I would access it, and do any work I need, and if it did not existed, then I would create it. The same goes for the deinitialization - each module would register, to track the use count, and the last one closed will perform the cleanup. – Jan Holecek Jan 06 '11 at 15:11
  • If its inter process thing you need special functionality provided in the platform. But if its "inside the current process", globally defined variables are the named memory. – 9dan Jan 06 '11 at 15:15
  • Wouldn't a global variables be shared in one module only, with the separate copies in other modules? – Jan Holecek Jan 06 '11 at 15:21
  • Well, your edit will allow me to check, if this is the first time I try to access/create the Singleton, but how do I store the created instance of the singleton object? – Jan Holecek Jan 06 '11 at 15:57
  • @John You are right. If we can't ensure the Singleton is really single, we also can't access the Singleton instance. We can overcome this problem by introduce inter-process shared memory or something. But I think its really overkill to go that far. – 9dan Jan 06 '11 at 17:35
  • #update2: providing 2 versions of the static library wouldn't be a problem - the only requirement is, they both provide the same basic functionality, but the server one could provide some more, or at least it can perform more actions at the background (initialization). I've planned to use at least 2 versions of the static libs - one for the executable, and second for the shared libraries, but now I'm not sure if that would be possible, due to the order of program initialization (see my comment at Ben Voigts answer). – Jan Holecek Jan 06 '11 at 19:06
0

As per MSDN I see there are only two ways to share data between modules

  1. Using data_seg pragma
  2. Use shared memory.

As someone pointed out Shared Segment works only for two instances of the same dll so we are left with only one choice to use Memory-Mapped Files technique.

irsis
  • 952
  • 1
  • 13
  • 33
0

You can use boost::interprocess http://www.boost.org/doc/libs/1_45_0/doc/html/interprocess.html and on Windows you can create a shared segment in your DLL that will be shared by all processes using #pragma's: http://www.codeproject.com/KB/DLL/data_seg_share.aspx

Gene Bushuyev
  • 5,512
  • 20
  • 19
  • Yes, I'm hoping it can be solved with boost, but could you be more specific how to create named memory segment in the current process only? – Jan Holecek Jan 06 '11 at 14:58
  • What do you mean by "current process only?" Accessible from one process only? Writable from one process only? – Gene Bushuyev Jan 06 '11 at 14:59
  • If I create a shared memory with some ID, it will be accessible from all the processes in the system, right? I need that memory to be accessible only by the process that creates it, with full read/write access – Jan Holecek Jan 06 '11 at 15:03
  • I don't understand. Why do you need the shared memory at all, if you have everything in one process? Memory is already accessible to every function in that process and it's already isolated from other processes. – Gene Bushuyev Jan 06 '11 at 15:31
  • 1
    Yes, memory is accessible, but how do I find out the address, where my hypothetical manager object resides? I can't use global variable, because we are talking about different program modules, that gets compiled separately. – Jan Holecek Jan 06 '11 at 15:42
  • Call a function exported from that module to obtain it – paulm Aug 20 '13 at 15:48