LibrePCB Developers Documentation
DirectoryLock Class Referencefinal

This class can be used to implement file-based directory locks. More...

#include <directorylock.h>

+ Collaboration diagram for DirectoryLock:

Public Types

enum  LockStatus
 The return type of getStatus() More...
 
typedef std::function< bool(const FilePath &dir, LockStatus status, const QString &user)> LockHandlerCallback
 Callback type used to determine whether a lock should be overridden or not. More...
 

Public Member Functions

 DirectoryLock () noexcept
 The default constructor. More...
 
 DirectoryLock (const DirectoryLock &other)=delete
 Copy constructor. More...
 
 DirectoryLock (const FilePath &dir) noexcept
 A constructor which will call setDirToLock() More...
 
 ~DirectoryLock () noexcept
 The destructor (this may also unlock the locked file) More...
 
void setDirToLock (const FilePath &dir) noexcept
 Specify the directory for which you need the lock. More...
 
const FilePathgetDirToLock () const noexcept
 Get the filepath of the directory to lock (passed by setDirToLock()) More...
 
const FilePathgetLockFilepath () const noexcept
 Get the filepath of the lock file (NOT the directory to lock!) More...
 
LockStatus getStatus (QString *lockedByUser=nullptr) const
 Get the lock status of the specified directory. More...
 
void tryLock (LockHandlerCallback lockHandler=nullptr)
 Lock the specified directory if not already locked. More...
 
bool unlockIfLocked ()
 Unlock the specified directory if it was locked by this object. More...
 
void lock ()
 Lock the specified directory (create/update the lock file) More...
 
void unlock ()
 Unlock the specified directory (remove the lock file) More...
 
DirectoryLockoperator= (const DirectoryLock &rhs)=delete
 

Static Private Member Functions

static QSet< FilePath > & dirsLockedByThisAppInstance () noexcept
 Get the global set of filepaths locked by this application instance. More...
 

Private Attributes

FilePath mDirToLock
 The filepath to the directory to lock (passed by setDirToLock()) More...
 
FilePath mLockFilePath
 The filepath to the lock file. More...
 
bool mLockedByThisObject
 This attribute defines if the lock is active by this object. More...
 

Detailed Description

This class can be used to implement file-based directory locks.

Many classes of this project open some directories (workspaces, projects, library elements, ...). But it's very dangerous if a directory is opened multiple times simultaneously (by the same or another instance of the application, maybe even on different computers if the directories are located on a network drive). To avoid such problems, this class provides a mechanism to create directory locks.

How such a directory lock works:

Let's say that you want to open the directory "/foo/bar/". Then a lock file with the filepath "/foo/bar/.lock" will be created. After closing the directory, the lock file will be removed. So, while the directory (e.g. a project) is open, there will be a lock file in the same directory. If the same or another instance of the application now wants to open the same directory at the same time, the lock file is detected and opening the directory will be denied.

The lock file is a simple UTF-8 encoded text file with 5 lines with following values:

  1. The full name (first name + last name) of the user which holds the lock
  2. The username (logon name) of the user which holds the lock
  3. The hostname of the user's computer which holds the lock
  4. The process id (PID) of the application instance which holds the lock
  5. The process name of the application instance which holds the lock
  6. The datetime when the lock file was created/updated (UTC and ISO format!)

Example:

Homer Simpson
homer
homer-workstation
1234
2013-04-13T12:43:52Z

The lock file (and especially its content) is also used to detect application crashes. If the application crashes while a directory was locked, the lock file will still exist after the application crashed. Now, if the user tries to open the locked directory again, the content of the lock file will be parsed. If the username and the hostname in the lock file is equal to the current user which tries to get the lock, it's clear that the lock file does NOT exist because the locked directory is already open, but that the application crashed while the directory was locked. If there exists a backup of the locked directory (e.g. project auto-save), this allows to ask the user whether the backup should be restored or not.

How to use this class:

First, you need to create an instance of this class for the directory you want to protect with a lock. There are two different constructors for this purpose. If you use the default constructor, you need to call setDirToLock() afterwards. Now you can read the lock status of the specified directory with getStatus(). With lock() you can create the lock file, and with unlock() you can remove the lock file. There are also the two convenience methods tryLock() and unlockIfLocked(), just read their documentation for more information.

Note
The destructor will automatically call unlockIfLocked(). This allows a reliable implementation of a directory lock, because you can add a DirectoryLock instance to the attributes of your class which access a directory which should be locked. This will ensure that the lock will be released when your object gets destroyed (RAII). See the code example below.

Code Example:

// a class which opens a directory and needs to lock it
class MyDirectoryOpeningClass {
public:
MyDirectoryOpeningClass() // constructor
: mLock(FilePath("C:/myDirectory")) { // variant 1 to set the filepath
mLock.setDirToLock(FilePath("C:/myDirectory")); // variant 2
switch (mLock.getStatus()) { // Note: this line can throw an exception!
// No lock exists --> lock the directory now
mLock.lock(); // Note: this line can throw an exception!
break;
// The directory is (probably) already locked by another instance!
throw Exception("Directory is locked!");
// The application crashed while the lock was active.
// Ask the user whether a backup should be restored or not.
break;
default:
// Should not happen...
break;
}
// if you don't care about stale locks, you could just do this instead:
// myLock.tryLock(); // Note: this line can throw an exception!
}
~MyDirectoryOpeningClass() {
// You do not have to (but you could) call myLock.unlockIfLocked(),
// as it will be called automatically in the destructor of myLock.
// try { myLock.unlockIfLocked(); } catch (...) { }
}
private:
// an instance, not only a pointer (important for RAII)!
};

Member Typedef Documentation

◆ LockHandlerCallback

typedef std::function<bool(const FilePath& dir, LockStatus status, const QString& user)> LockHandlerCallback

Callback type used to determine whether a lock should be overridden or not.

Parameters
dirThe directory to be locked.
statusThe current status of the lock (see getStatus()).
userName of the user which currently holds the lock.
Return values
trueOverride lock.
falseDo not override lock.
Exceptions
librepcb::UserCanceledto abort locking the directory.

Member Enumeration Documentation

◆ LockStatus

enum LockStatus
strong

The return type of getStatus()

Enumerator
Unlocked 

The directory is not locked (lock file does not exist).

StaleLock 

The directory is locked by a crashed application instance.

LockedByThisApp 

The directory is locked by this application instance.

LockedByOtherApp 

The directory is locked by another application instance on this machine.

LockedByOtherUser 

The directory is locked by another user or machine.

LockedByUnknownApp 

The directory is locked by an unknown application (may be stale).

Constructor & Destructor Documentation

◆ DirectoryLock() [1/3]

DirectoryLock ( )
noexcept

The default constructor.

Warning
If you use this constructor, you need to call setDirToLock() afterwards (before calling any other method of this class)!

◆ DirectoryLock() [2/3]

DirectoryLock ( const DirectoryLock other)
delete

Copy constructor.

Parameters
otherThe object to copy

◆ DirectoryLock() [3/3]

DirectoryLock ( const FilePath dir)
explicitnoexcept

A constructor which will call setDirToLock()

Parameters
dirSee setDirToLock()
+ Here is the call graph for this function:

◆ ~DirectoryLock()

~DirectoryLock ( )
noexcept

The destructor (this may also unlock the locked file)

Note
The destructor will also try to unlock the directory if it was locked with this object.
+ Here is the call graph for this function:

Member Function Documentation

◆ setDirToLock()

void setDirToLock ( const FilePath dir)
noexcept

Specify the directory for which you need the lock.

Parameters
dirThe filepath to the directory to lock
Warning
This method must not be called when this object already holds a lock!
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getDirToLock()

const FilePath& getDirToLock ( ) const
inlinenoexcept

Get the filepath of the directory to lock (passed by setDirToLock())

Returns
The filepath to the directory to lock (invalid if no filepath was set)

◆ getLockFilepath()

const FilePath& getLockFilepath ( ) const
inlinenoexcept

Get the filepath of the lock file (NOT the directory to lock!)

Returns
The filepath to the lock file (invalid if no valid filepath was set)
+ Here is the call graph for this function:

◆ getStatus()

DirectoryLock::LockStatus getStatus ( QString *  lockedByUser = nullptr) const

Get the lock status of the specified directory.

Parameters
lockedByUserIf not nullptr and the directory is locked, the username of the current lock is written into this string.
Returns
The current lock status (see LockStatus)
Exceptions
Exceptionon error (e.g. invalid filepath, no access rights, ...)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ tryLock()

void tryLock ( LockHandlerCallback  lockHandler = nullptr)

Lock the specified directory if not already locked.

This is a save method to get a lock without the need for first reading the lock status with getStatus(). Depending on the lock status, this method does following:

  • Unlocked: Set "wasStale = false" and get the lock (calling lock())
  • StaleLock: Set "wasStale = true" and get the lock (calling lock())
  • Locked: Throw exception (something like "Directory already locked")
Parameters
lockHandlerIf supplied and the directory is already locked, this callback gets called to determine whether the lock should be overridden or not. If not supplied and the directory is locked, an exception will be thrown.
Exceptions
Exceptionon error (e.g. already locked, no access rights, ...)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ unlockIfLocked()

bool unlockIfLocked ( )

Unlock the specified directory if it was locked by this object.

If the specified directory is locked by this object, this method calls unlock(). Otherwise this method does nothing.

Returns
True if the lock has been released by this object, false otherwise.
Exceptions
Exceptionon error (e.g. invalid filepath, no access rights, ...)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ lock()

void lock ( )

Lock the specified directory (create/update the lock file)

Warning
This method will always overwrite an already existing lock file, even if it was created by another application instance! So: Always check the lock status first with getStatus(), or use tryLock() instead!
Exceptions
Exceptionon error (e.g. invalid filepath, no access rights, ...)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ unlock()

void unlock ( )

Unlock the specified directory (remove the lock file)

Warning
This method will always remove an existing lock file, even if it was created by another application instance! So: Always check the lock status first with getStatus(), or use unlockIfLocked() instead!
Exceptions
Exceptionon error (e.g. invalid filepath, no access rights, ...)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ operator=()

DirectoryLock& operator= ( const DirectoryLock rhs)
delete
+ Here is the caller graph for this function:

◆ dirsLockedByThisAppInstance()

QSet< FilePath > & dirsLockedByThisAppInstance ( )
staticprivatenoexcept

Get the global set of filepaths locked by this application instance.

Returns
Set of directory filepaths currently locked by this instance.
+ Here is the caller graph for this function:

Member Data Documentation

◆ mDirToLock

FilePath mDirToLock
private

The filepath to the directory to lock (passed by setDirToLock())

◆ mLockFilePath

FilePath mLockFilePath
private

The filepath to the lock file.

Example: If the filepath "/foo/bar" was passed to setDirToLock(), this attribute will have the value "/foo/bar/.lock".

◆ mLockedByThisObject

bool mLockedByThisObject
private

This attribute defines if the lock is active by this object.

If lock() was called successfully, mLockedByThisObject is set to true. If unlock() was called successfully, mLockedByThisObject is set to false.

In other words: This attribute is true while this object has the ownership over the lock file (between calling lock() and unlock()).

The only goal of this attribute is to decide whether the destructor should remove the lock or not. If the destructor is called while this attribute is true, the destructor will call unlock() to remove the file lock.


The documentation for this class was generated from the following files: