omni::extras::UniqueApp

Defined in omni/extras/UniqueApp.h

class UniqueApp

Helper class to manage a unique app.

A unique app is one that is intended to only run a single instance of it at any given time. This contains helper functions to handle common tasks that are intended to be used on both the unique app’s side and on the host app side. This contains two major sets of helper functions.

  • One set is to manage the uniqueness of the app itself. These can either be called entirely from within the unique app process after launch to determine if another instance of the app is already running and that the new one should exit immediately. Alternatively, the launching host app could also first check if the unique app process is already running before deciding to launch it in the first place.

  • The other set is to manage notifying the unique app process when it should exit naturally. These functions setup a signal that a host app is still running that the unique app can then poll on. The host app is responsible for setting its ‘running’ signal early on its launch. The unique app process will then periodically poll to see if any host apps are still running.

Note that a ‘connection’ to the unique app is not actually a connection in a communication sense. This is more akin to a reference count on an object that the operating system will manage regardless of how the host app exits or stops running. The ‘connection’ can exist for the host app before the unique app is even launched (in fact, this behavior is preferred since it simplifies polling and avoids the possibility of an unintentional early exit). The host app’s ‘connection’ to the unique app does not require any actual communication, just that the connection object remain open until it is either explicitly closed or the host app process exits.

Public Functions

UniqueApp() = default

Constructor: creates a new unique app object with default settings.

This creates a new object with all default settings. Note that this will use the default guard name. If the caller doesn’t set a different guard name with setGuardName(), the uniqueness of the app represented by this object may conflict with other apps that also use that name.

UniqueApp(const UniqueApp&) = delete
inline UniqueApp(UniqueApp &&other)

Move-constructor: initializes this object by moving information from another object.

Remark

This moves the information from another object into this one. The other object will still be valid but will have been reset to an empty state.

Parameters

other[inout] The other object to move data from.

inline UniqueApp &operator=(UniqueApp &&other)

Moves data from another object into this one.

Remark

This moves the information from another object into this one. The other object will still be valid but will have been reset to an empty state. Upon return, this object will only be as valid as the other object was.

Parameters

other[inout] The other object to move data from.

inline UniqueApp(const char *guardPath, const char *guardName)

Constructor: creates a new unique app object with explicit settings.

This creates a new object with explicit settings for the guard path and guard names. This is a convenience shortcut for creating an object with default settings, then changing the guard path and guard name manually using setGuardPath() and setGuardName().

Parameters
  • guardPath[in] The directory to store the various guard files in. This may not be nullptr. This may be a relative or absolute path. If this is an empty string, the current directory will be used instead.

  • guardName[in] The prefix to use for the guard objects. This may not be nullptr. If this is an empty string, the default prefix will be used. This should be a name that is unique to the app being launched. However, all instances trying to launch that single unique app must use the same guard name.

inline ~UniqueApp()
inline void setGuardPath(const char *path)

Sets the path to put the guard file(s) in.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure this call is protected.

Note

If the given path or any components on it do not exist at the time a guard file is being created, any missing directories will be created. This only occurs when a guard file is being used however, not directly during this call.

Parameters

path[in] The directory to store the various guard files in. This may not be nullptr. This may be a relative or absolute path. If this is an empty string, the current directory will be used instead.

Returns

no return value.

inline void setGuardName(const char *name)

Sets the name for the guard file(s).

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure this call is protected.

Parameters

name[in] The prefix to use for the guard objects. This may not be nullptr. If this is an empty string, the default prefix will be used. This should be a name that is unique to the app being launched. However, all instances trying to launch that single unique app must use the same guard name.

Returns

no return value.

inline bool createLaunchGuard()

Creates the run guard object for the unique app.

This creates the run guard object that is used to determine if the unique app is already running. This is intended to be called exactly once from the unique app’s process early during its startup. The guard object that is created will exist for the remaining lifetime of the unique app’s process. Once the process that calls this exits, the object will be cleaned up by the operating system.

The checkLaunchGuard() function is intended to be used to determine if this object still exists. It may be called either from within the unique app itself to determine if new launches of it should just exit, or it may called from a host app to determine whether to launch the unique app in the first place. This function however is intended to be called from the unique app itself and acts as both the guard object creation and a check for its existence.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure calls to it are protected. However, since this is intended to work between multiple processes, thread safety is not necessarily the main concern and a race condition may still be possible. This is intended to only be called once from the creating process.

Returns

true if the unique app launch guard was newly created successfully. Returns false if the launch guard could not be created or it was already created by another process.

inline void destroyLaunchGuard()

Destroys the locally created launch guard object.

This destroys the launch guard object that was most recently created with createLaunchGuard(). The launch guard object can be recreated with another call to createLaunchGuard() later.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure any calls are protected. However, note that since this is a global object potentially used for interprocess operations, there may still be a possible race condition with its use.

Returns

No return value.

inline bool checkLaunchGuard()

Tests whether the unique app is already running.

This tests whether the unique app is currently running. This is done by trying to detect whether the guard object exists in the system. When the unique app process exits (naturally or otherwise), the operating system will remove the guard object automatically.

This call differs from createLaunchGuard() in that this will not create the guard object if it does not already exist. This is intended to be used from host apps before launching the unique app instead of checking for uniqueness from the unique app itself.

Thread Safety

This call is technically thread safe on Windows. On Linux it is thread safe as long as setGuardPath() is not called concurrently. However, since it is meant to test for an object that is potentially owned by another process there may still be race conditions that could arise.

Returns

true if the unique app is currently running. Returns false if the unique app is not running or if the guard object could not be accessed.

inline bool connectClientProcess()

Notifies the unique app that a host app is running.

This lets the unique app know that the calling host app is still running. This is done by adding a shared lock reference to a marker file that the unique app can poll on periodically. The operating system will automatically remove the lock reference(s) for this call once the calling process exits (naturally or otherwise).

This is intended to be called only once by any given host app. However, it may be called multiple times without much issue. The only downside to calling it multiple times would be that extra handles (Windows) or file descriptors (Linux) will be consumed for each call.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure calls to it are protected. However, since it is meant to operate between processes, there may still be unavoidable race conditions that could arise.

Returns

true if the unique app was successfully notified of the new running host app. Returns false if the notification either couldn’t be sent or could not be completed.

inline void disconnectClientProcess()

‘Disconnect’ the calling process from the exit guard.

This closes the calling process’s reference to the exit guard file. This will allow the exit guard for a host app process to be explicitly cleaned up before exit if needed.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure calls to it are protected.

Returns

No return value.

inline bool haveAllClientsExited()

Tests whether all ‘connected’ host apps have exited.

This tests whether all ‘connected’ host apps have exited. A host app is considered to be ‘connected’ if it had called connectClientProcess() that had succeeded. Once a host app exits, all of its ‘connections’ to the unique app are automatically cleaned up by the operating system.

This is intended to be called from the unique app periodically to determine if it should exit itself. The expectation is that this would be called once per minute or so to check whether it should be shut down. When this does succeed, the unique app should perform any final cleanup tasks then exit itself.

Thread Safety

This call is not thread safe. It is the caller’s responsibility to ensure calls to it are protected. However, since it is meant to operate between processes, there may still be unavoidable race conditions that could arise.

Returns

true if all connected host apps have exited (naturally or otherwise). Returns false if at least one host app is still running.