Thread overview
Restricting D applications to a single instance
Feb 22, 2021
Preetpal
Feb 22, 2021
Preetpal
Feb 22, 2021
Preetpal
Feb 22, 2021
Ali Çehreli
Feb 22, 2021
Preetpal
February 22, 2021
I want to restrict a D application to a single instance. Is there a way to do this using the D standard library?

I know this can be done using named mutexes on Windows using Windows-specific APIs but I want to avoid this in general because I want to port the code to FreeBSD without introducing another code path for this functionality. I also know that server applications lock PID files to achieve this but I would like to avoid creating temporary files.

I want to implement this functionality in the following code: https://gist.github.com/preetpalS/d2482d6ec91eb8147e6cff43ab197ed5


February 21, 2021
On 2/21/21 9:29 PM, Preetpal wrote:
> I want to restrict a D application to a single instance. Is there a way to do this using the D standard library?

When you say "application", you mean a class or type?

If so, you can do this:

class C
{
   private this() { /*ctor code for single instance */ }
   private static C _instance = new C();
   public C instance() { return _instance; }
}

You can also do it with a struct, but it's not as bullet-proof:

struct S
{
  @disable this();
  private static S _instance;
  ref S instance() { return _instance; }
}

But one can always do this, and the compiler will never prevent it:

S s = S.init;

All these have an instance per thread.

If you want a global singleton that is initialized on first use, use the D singleton pattern: https://wiki.dlang.org/Low-Lock_Singleton_Pattern

-Steve
February 22, 2021
On Monday, 22 February 2021 at 02:39:58 UTC, Steven Schveighoffer wrote:
> On 2/21/21 9:29 PM, Preetpal wrote:
>> I want to restrict a D application to a single instance. Is there a way to do this using the D standard library?
>
> When you say "application", you mean a class or type?

I actually meant that you cannot have two instances of an application (executable) running at once.

So for example, if the functionality were implemented for a text editor, you would only be able to open one process (or instance) of that text editor. Like if you were to double click an executable on Windows in file explorer repeatedly, it would only open once, not multiple times.
February 22, 2021
On Monday, 22 February 2021 at 02:39:58 UTC, Steven Schveighoffer wrote:
> On 2/21/21 9:29 PM, Preetpal wrote:
>> I want to restrict a D application to a single instance. Is there a way to do this using the D standard library?
>
> When you say "application", you mean a class or type?
>

I decided to implement what I meant (single application (executable) instance restriction) using the Windows API (since my application only runs on Windows currently anyways):

    // Ensures only one instance of application can run at one time
    HANDLE globalMutexHandle = CreateMutex(NULL, true, "multi-monitor-window-maximization");
    if (globalMutexHandle == NULL) {
        return -1;
    }
    DWORD lastErrorAfterMutexCreationAttempt = GetLastError();
    if (lastErrorAfterMutexCreationAttempt == ERROR_ALREADY_EXISTS) {
        return -2;
    } if (lastErrorAfterMutexCreationAttempt == ERROR_INVALID_HANDLE) {
        return -3;
    }
    scope(exit) {
        ReleaseMutex(globalMutexHandle);
        CloseHandle(globalMutexHandle);
    }

This prevents someone from opening my particular application twice (so there cannot be two processes running of the same application/executable). I also updated by Github GIST code with the update.
February 22, 2021
On 2/21/21 7:28 PM, Preetpal wrote:

> I decided to implement what I meant (single application (executable)
> instance restriction) using the Windows API (since my application only
> runs on Windows currently anyways):

I achieve it with 'flock' (man 2 flock) on Linux. My case is different thoug: It allows me to have single writer and many readers of program data, which is kept inside a cache directory. All instances start with read permissionss to exclude any potential writer. Once a writer grabs the lock for writing, no other writer or reader can exist, etc. A nice side effect that I realized was since there is only one writer, the lock file itself stores progress information so that other instances can say something like "Waiting for the writer to finish: 75%" by reading from the lock file itself. :)

Works like a charm except flock operations are not atomic; so some care is needed. For example, the writer cannot assume there were no other writers when it "drops" its access right to a reader. It has to check some signature to make sure that everything is expected.

Ali

February 22, 2021
On Monday, 22 February 2021 at 08:00:06 UTC, Ali Çehreli wrote:
> I achieve it with 'flock' (man 2 flock) on Linux. My case is different thoug: It allows me to have single writer and many readers of program data, which is kept inside a cache directory. All instances start with read permissionss to exclude any potential writer. Once a writer grabs the lock for writing, no other writer or reader can exist, etc. A nice side effect that I realized was since there is only one writer, the lock file itself stores progress information so that other instances can say something like "Waiting for the writer to finish: 75%" by reading from the lock file itself. :)
>
> Works like a charm except flock operations are not atomic; so some care is needed. For example, the writer cannot assume there were no other writers when it "drops" its access right to a reader. It has to check some signature to make sure that everything is expected.

Implementing progress reporting in that way is cool. :)

I'll looking into using flock to implement this functionality when I port this program to FreeBSD/Linux (I'll probably try implementing this functionality in another application first as I only infrequently use those operating systems as desktop environments).