Looks simple enough, and it's quite useful; the same API as built-in .NET Semaphore. Obviously, we can’t change the IIgnite interface, so GetOrCreateSemaphore is an extension method. Now onto the implementation!

Java Plugin

Let’s start with Java side of things. We need a way to call the Ignite.semaphore() method there and provide access to the resulting instance to the .NET platform.

Then comes the plugin entry point: PluginProvider<PluginConfiguration>. This interface has lots of methods, but most of them can be left empty (name and version must not be null, so put something in there). We are interested only in the initExtensions method, which allows us to provide a cross-platform interoperation entry point. This is done by registering the PlatformPluginExtension implementation:

PlatformPluginExtension has a unique id to retrieve it from the .NET side and a PlatformTarget createTarget() method to create an object that can be invoked from .NET.

PlatformTarget interface in Java mirrors IPlatformTarget interface in .NET. When you call IPlatformTarget.InLongOutLong in .NET, PlatformTarget.processInLongOutLong is called in Java on your implementation. There are a number of other methods that allow exchanging of primitives, serialized data, and objects. Each method has a type parameter which specifies an operation code, in case when there are many different methods on your plugin.

We are going to need two PlatformTarget classes: one that represents our plugin as a whole and has the getOrCreateSemaphore method, and another one to represent each particular semaphore. The first one should take a string name and int count and return an object, so we need to implement PlatformTarget.processInStreamOutObject. The other methods are not needed and can be left blank:

For each ISemaphore object in .NET, there will be one IgniteNetSemaphore in Java, which is also a PlatformTarget. This object will handle WaitOne and Release methods and delegate them to an underlying IgniteSemaphore object. Since both of these methods are void and parameterless, the simplest PlatformTarget method will work:

That’s it, the Java part is implemented! We just need to make our IgniteNetSemaphorePluginProvider class available to the Java service loader by creating a resources\META-INF.services\org.apache.ignite.plugin.PluginProvider file with a single line containing the class name. Package the project with Maven (mvn package in the console, or use IDEA UI). There should be a IgniteNetSemaphorePlugin-1.0-SNAPSHOT.jar file in the target directory. We can move on to the .NET part now.

.NET Plugin

First, let’s make sure our Java code gets picked up by Ignite. Create a console project, install the Ignite NuGet package, and start Ignite with the path to the jar file that we just created:

For the GetPlugin method to work, the IgniteConfiguration.PluginConfigurations property should be set. It takes a collection of IPluginConfiguration implementations, and each implementation must, in turn, link to an IPluginProvider implementation with an attribute:

On node startup, Ignite.NET iterates through plugin configurations, instantiates plugin providers, and calls the Start(IPluginContext<SemaphorePluginConfiguration> context) method on them. IIgnite.GetPlugin calls are then delegated to IPluginProvider.GetPlugin of the provider with the specified name.

IPluginContext provides access to the Ignite instance, Ignite and plugin configurations, and has theGetExtension method, which delegates to PlatformPluginExtension.createTarget() in Java. This way we “establish connection” between the two platforms. IPlatformTarget in .NET gets linked to PlatformTarget in Java; they can call each other, and the lifetime of the Java object is tied to the lifetime of the .NET object. Once the .NET object is reclaimed by the garbage collector, finalizer releases the Java object reference, and it will also be garbage collected.

The remaining implementation is simple - just call the appropriate IPlatformTarget methods:

We are done! Quite a bit of boilerplate code, but adding more logic to the existing plugin is easy, just implement a pair of methods on both sides. Ignite uses JNI and unmanaged memory to exchange data between .NET and Java platforms within a single process, which is simple and efficient.

Testing

To demonstrate the distributed nature of our Semaphore, we can run multiple Ignite nodes where each of them calls WaitOne(). We’ll see that only two nodes at a time are able to acquire the semaphore: