2

I'm using an unmanaged library (written in C++) in my code (C#). All the methods provided by the library are static. I use P/Invoke to communicate with the library. Here's the way the library works:

  1. There's an initialization method that you need to call before calling all the other methods (this takes one parameter and that parameter can only be set during initialization).
  2. There's a settings method to change different settings for the library, you need to run this if you need to change the way the library works (more like fine-tuning the library). Settings can be changed any time.
  3. There is a method that takes an array of numbers, and returns another array. Let's call this the intermediate operation. This operation takes a while, so I'd like to cache it and prevent calculating it every time.
  4. The array returned by the previous method (intermediate operation) is an input to the last method that creates the result. Let's call this the main operation

I'd normally like to have different instances of the library, one with settings A and the other one with settings B. As the library is static, I can't.

How can I wrap this library in an instance class? Probably, every instance of my class will need to load a new instance of the library in memory (as every instance of the library itself is static, and you can't have two different setting in one instance of the library).

Is there a workaround for this, other than re-writing the library (as I don't have any control on how it's written)?

And I'd really like to be able to handle parallel calls to operation methods.

hattenn
  • 4,371
  • 9
  • 41
  • 80
  • Do you need the result of the intermediate operation except for passing it into the main operation? – Andre Loker Feb 13 '13 at 10:57
  • Yeah I need it for caching purposes. It takes a while to create, and I'd like to cache it. – hattenn Feb 13 '13 at 10:58
  • Are you able to synchronize access to the library and call `SetSettings` each time? (or at least when someone uses a different "instance" that was last used) – Justin Feb 13 '13 at 11:03
  • Is it possible to change the settings between calls? If so, do both the intermediate operation and the main operation rely on these settings to be the same for corresponding calls? What comes to mind is a class whose objects store the settings and apply them to the lib before each operation. To speed things up you could check for the current settings (wrap the lib by something that stores a unique ID/hash for the applied settings) and apply the stored settings only if needed. – Arne Mertz Feb 13 '13 at 11:04
  • That would have been my suggestion as well. – Andre Loker Feb 13 '13 at 11:05
  • When the library is loaded, I have to set the settings. When the program terminates, all the settings are reset as it doesn't have any way of remembering the last settings. So it needs to be set each time it's loaded with the program. – hattenn Feb 13 '13 at 11:05
  • Yeah it's possible to change settings between each call, I think I misunderstood Justin. @ArneMertz, your suggestion sounds good, but it would be impossible to make it thread-safe. And I'm not sure how costly it would be to change the settings. – hattenn Feb 13 '13 at 11:09
  • And by the way, if those two instance are initialized differently (initialization takes a parameter too), I need to re-initialize the library each time a method is called from a different instance. – hattenn Feb 13 '13 at 11:09
  • a dirty workaround would be to wrap the unmanaged library with an executing project, and then run each "instance" of the static class in a different process. Drawbacks are of course the overhead of a multi-process program only to avoid re-configuring the static class. – eladidan Feb 13 '13 at 11:12
  • I have a similar type of project. What I do is store a native `map`. There is a static method to create an instance of `MyNativeClass`, and it returns it's ID as an `int`. Then I can call instance methods using my exported static method, by passing the correct ID of the instance I want to operate on. – Rotem Feb 13 '13 at 11:21
  • if all else fails, try this http://stackoverflow.com/questions/4225064/load-dll-multiple-times-to-allow-multi-threading-in-net approach, but it's actually a hack, so i wouldn't do it unless nothing else works. – thang Feb 13 '13 at 11:35

2 Answers2

1

Here's an example implementation of what's been said in the comments to the question:

public class Library
{
    private static class Native
    {
        [DllImport("foobar.dll")]
        public static extern void Initialize();

        [DllImport("foobar.dll")]
        public static extern void Settings(int param1, string param2);

        [DllImport("foobar.dll")]
        public static extern float[] Intermediate(float[] input);

        [DllImport("foobar.dll")]
        public static extern void MainMethod(float[] main);
    }

    private static readonly object sync = new object();

    private int param1;
    private string param2;

    static Library()
    {
        Native.Initialize();
    }


    public void Settings(int param1, string param2)
    {
        this.param1 = param1;
        this.param2 = param2;
    }

    public float[] Intermediate(float[] input)
    {
        lock (sync)
        {
            Native.Settings(param1, param2);
            return Native.Intermediate(input);
        }
    }

    public void MainMethod(float[] input)
    {
        lock (sync)
        {
            Native.Settings(param1, param2);
            Native.MainMethod(input);
        }
    }
}

The most important things

  • Settings does not call the "real" settings immediately, but stores the values
  • When Intermediate and MainMethod are called they apply the settings store previously
  • Both calls are synchronized on a static object to avoid that calls to different instances of Library running on different threads interfere.
Andre Loker
  • 8,368
  • 1
  • 23
  • 36
  • Nice suggestion for sure. But first thing, `initialize` takes a parameter too, so it needs to be re-initialized in each call. And the second thing is, is there an easy way to parallelize this? As far as I understand, it can only be called by one thread. – hattenn Feb 13 '13 at 11:12
  • I didn't know about the requirement that initialize takes a parameter, sorry. You're right, this won't parallelize. You could try to create multiple AppDomains. Managed static variables exist once per AppDomain. I'm not sure about static variables in native DLLs. Might be worth to further investigate, though. Otherwise, you can always resort to a multi-process solution, although that's harder to coordinate, of course. Edit - nope, AppDomains won't work according to this http://stackoverflow.com/questions/6011518/using-appdomains-to-parallelize-non-thread-safe-dll – Andre Loker Feb 13 '13 at 11:25
  • 1
    You can copy the DLL multiple times with different names and then load them dynamically. That could actually work. – Andre Loker Feb 13 '13 at 11:28
  • @AndreLoker How would that work? `DllImport` attributes need to be resolved at compile time. – Rotem Feb 13 '13 at 11:29
  • 1
    See this: http://stackoverflow.com/questions/6452951/how-to-dynamically-load-and-unload-a-native-dll-file – Andre Loker Feb 13 '13 at 11:29
  • @AndreLoker If you can put your comments (the part about dynamic loading and achieving multiple loading of the libraries with copying of the library), I'll pick this as the accepted answer. Better would be to write a new answer, this one could be useful to some other people that will come across this question. – hattenn Feb 16 '13 at 20:00
0

As I suggested in my comment, if palatalization is required, the only workaround I can think of is to go with a multi-process approach: wrapping each "settings" in an independent process. Of course that will make communication and data-traffic between processes the new candidate for a bottle-neck (whether you will be using WCF named pipes, Remoting, WM_COPYDATA or sockets).

eladidan
  • 2,634
  • 2
  • 26
  • 39
  • actually you can use shared memory, which is pretty good for ipc. however, there is a small chance that you can load the same dll multiple times using loadlibraryex... – thang Feb 13 '13 at 11:52
  • @thang: unfortunately (for the OP) you can't: http://stackoverflow.com/questions/3497516/does-loadlibrary-create-distinct-instances – eladidan Feb 13 '13 at 11:57
  • fortunately, there may be a way around it: http://stackoverflow.com/questions/4225064/load-dll-multiple-times-to-allow-multi-threading-in-net – thang Feb 13 '13 at 11:58
  • who knows... it's accepted, so i assume he tried it. not sure. – thang Feb 13 '13 at 12:23