aboutsummaryrefslogtreecommitdiff
path: root/nix/libstore/pathlocks.cc
blob: 9797ddd7abf12e29886ee6032003f7bde68ece2c (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include "pathlocks.hh"
#include "util.hh"

#include <cerrno>
#include <cstdlib>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


namespace nix {


int openLockFile(const Path & path, bool create)
{
    AutoCloseFD fd;

    fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0600);
    if (fd == -1 && (create || errno != ENOENT))
        throw SysError(format("opening lock file `%1%'") % path);

    closeOnExec(fd);

    return fd.borrow();
}


void deleteLockFile(const Path & path, int fd)
{
    /* Get rid of the lock file.  Have to be careful not to introduce
       races.  Write a (meaningless) token to the file to indicate to
       other processes waiting on this lock that the lock is stale
       (deleted). */
    unlink(path.c_str());
    writeFull(fd, "d");
    /* Note that the result of unlink() is ignored; removing the lock
       file is an optimisation, not a necessity. */
}


bool lockFile(int fd, LockType lockType, bool wait)
{
    struct flock lock;
    if (lockType == ltRead) lock.l_type = F_RDLCK;
    else if (lockType == ltWrite) lock.l_type = F_WRLCK;
    else if (lockType == ltNone) lock.l_type = F_UNLCK;
    else abort();
    lock.l_whence = SEEK_SET;
    lock.l_start = 0;
    lock.l_len = 0; /* entire file */

    if (wait) {
        while (fcntl(fd, F_SETLKW, &lock) != 0) {
            checkInterrupt();
            if (errno != EINTR)
                throw SysError(format("acquiring/releasing lock"));
        }
    } else {
        while (fcntl(fd, F_SETLK, &lock) != 0) {
            checkInterrupt();
            if (errno == EACCES || errno == EAGAIN) return false;
            if (errno != EINTR) 
                throw SysError(format("acquiring/releasing lock"));
        }
    }

    return true;
}


/* This enables us to check whether are not already holding a lock on
   a file ourselves.  POSIX locks (fcntl) suck in this respect: if we
   close a descriptor, the previous lock will be closed as well.  And
   there is no way to query whether we already have a lock (F_GETLK
   only works on locks held by other processes). */
static StringSet lockedPaths; /* !!! not thread-safe */


PathLocks::PathLocks()
    : deletePaths(false)
{
}


PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
    : deletePaths(false)
{
    lockPaths(paths, waitMsg);
}


bool PathLocks::lockPaths(const PathSet & _paths,
    const string & waitMsg, bool wait)
{
    assert(fds.empty());
    
    /* Note that `fds' is built incrementally so that the destructor
       will only release those locks that we have already acquired. */

    /* Sort the paths.  This assures that locks are always acquired in
       the same order, thus preventing deadlocks. */
    Paths paths(_paths.begin(), _paths.end());
    paths.sort();
    
    /* Acquire the lock for each path. */
    foreach (Paths::iterator, i, paths) {
        checkInterrupt();
        Path path = *i;
        Path lockPath = path + ".lock";

        debug(format("locking path `%1%'") % path);

        if (lockedPaths.find(lockPath) != lockedPaths.end())
            throw Error("deadlock: trying to re-acquire self-held lock");

        AutoCloseFD fd;
        
        while (1) {

            /* Open/create the lock file. */
	    fd = openLockFile(lockPath, true);

            /* Acquire an exclusive lock. */
            if (!lockFile(fd, ltWrite, false)) {
                if (wait) {
                    if (waitMsg != "") printMsg(lvlError, waitMsg);
                    lockFile(fd, ltWrite, true);
                } else {
                    /* Failed to lock this path; release all other
                       locks. */
                    unlock();
                    return false;
                }
            }

            debug(format("lock acquired on `%1%'") % lockPath);

            /* Check that the lock file hasn't become stale (i.e.,
               hasn't been unlinked). */
            struct stat st;
            if (fstat(fd, &st) == -1)
                throw SysError(format("statting lock file `%1%'") % lockPath);
            if (st.st_size != 0)
                /* This lock file has been unlinked, so we're holding
                   a lock on a deleted file.  This means that other
                   processes may create and acquire a lock on
                   `lockPath', and proceed.  So we must retry. */
                debug(format("open lock file `%1%' has become stale") % lockPath);
            else
                break;
        }

        /* Use borrow so that the descriptor isn't closed. */
        fds.push_back(FDPair(fd.borrow(), lockPath));
        lockedPaths.insert(lockPath);
    }

    return true;
}


PathLocks::~PathLocks()
{
    try {
        unlock();
    } catch (...) {
        ignoreException();
    }
}


void PathLocks::unlock()
{
    foreach (list<FDPair>::iterator, i, fds) {
        if (deletePaths) deleteLockFile(i->second, i->first);

        lockedPaths.erase(i->second);
        if (close(i->first) == -1)
            printMsg(lvlError,
                format("error (ignored): cannot close lock file on `%1%'") % i->second);

        debug(format("lock released on `%1%'") % i->second);
    }

    fds.clear();
}


void PathLocks::setDeletion(bool deletePaths)
{
    this->deletePaths = deletePaths;
}


bool pathIsLockedByMe(const Path & path)
{
    Path lockPath = path + ".lock";
    return lockedPaths.find(lockPath) != lockedPaths.end();
}

 
}
165aba1f Ricardo Wurmus 2023-12-15gnu: r-degreport: Update to 1.38.5....* gnu/packages/bioconductor.scm (r-degreport): Update to 1.38.5. [propagated-inputs]: Add r-dendextend and r-stringi. Change-Id: I73fe47489449b46406b879dba0318d02dc3c7614 Ricardo Wurmus 2023-12-15gnu: r-cellid: Update to 1.10.1....* gnu/packages/bioconductor.scm (r-cellid): Update to 1.10.1. Change-Id: I74f0e39907eb293f84a2ee9d29ef5feab15b698a Ricardo Wurmus 2023-12-15gnu: r-biocversion: Update to 3.18.1....* gnu/packages/bioconductor.scm (r-biocversion): Update to 3.18.1. Change-Id: I844d321dc3716d2658ddd75a81d844f8b302af53 Ricardo Wurmus 2023-12-15gnu: r-bandits: Update to 1.18.1....* gnu/packages/bioconductor.scm (r-bandits): Update to 1.18.1. Change-Id: Ied13d156cb7762fd6f86a5a0cccff302b684530d Ricardo Wurmus 2023-12-15gnu: r-aseb: Update to 1.46.3....* gnu/packages/bioconductor.scm (r-aseb): Update to 1.46.3. Change-Id: I7fb905224840f598261fceba67207cd71e7a73d9 Ricardo Wurmus 2023-12-15gnu: r-anvil: Update to 1.14.1....* gnu/packages/bioconductor.scm (r-anvil): Update to 1.14.1. Change-Id: I2ea83e4a11a1268f222201480563fe1c9ce1060b Ricardo Wurmus 2023-12-15gnu: r-agimicrorna: Update to 2.52.0....* gnu/packages/bioconductor.scm (r-agimicrorna): Update to 2.52.0. Change-Id: I0f739590d115b5dc354833cdac2ba3c400ddd995 Ricardo Wurmus 2023-12-15gnu: r-rhdf5lib: Update to 1.24.1....* gnu/packages/bioconductor.scm (r-rhdf5lib): Update to 1.24.1. [propagated-inputs]: Add r-biocstyle and r-stringr. [native-inputs]: Add r-rmarkdown. Change-Id: I18c06543abee8e4a715f1ef120ce600562804225 Ricardo Wurmus 2023-12-15gnu: r-reportingtools: Update to 2.42.3....* gnu/packages/bioconductor.scm (r-reportingtools): Update to 2.42.3. [source]: Adjust snippet. [arguments]: Use minify-build-system in 'process-javascript phase. [native-inputs]: Remove r-knitr; add r-rmarkdown. Change-Id: I1c00e1a1d8095f40da29b57b8e82de493fcff26e Ricardo Wurmus 2023-12-15gnu: r-affycoretools: Update to 1.74.0....* gnu/packages/bioconductor.scm (r-affycoretools): Update to 1.74.0. Change-Id: I4197d73e62477bb64182181229c8a33f3048406a Ricardo Wurmus 2023-12-15gnu: r-abn: Update to 3.0.4....* gnu/packages/bioconductor.scm (r-abn): Update to 3.0.4. Change-Id: I15970d14205b32f254ecf2c5207e98d7289ac281 Ricardo Wurmus 2023-12-15gnu: r-fhtest: Update to 1.5.1....* gnu/packages/bioconductor.scm (r-fhtest): Update to 1.5.1. Change-Id: I08513b08ee2fc72ab00b372a013dbfa6ed064fc1 Ricardo Wurmus 2023-12-15gnu: r-biomartr: Update to 1.0.7....* gnu/packages/bioconductor.scm (r-biomartr): Update to 1.0.7. Change-Id: I7f02a92d86506b4fbca950a13ed106753e1b7f76 Ricardo Wurmus 2023-12-15gnu: r-interactivedisplay: Add missing input....* gnu/packages/bioconductor.scm (r-interactivedisplay)[propagated-inputs]: Add r-biocmanager. Change-Id: I7e29c28202496ba58ec35adf03b77ac84691e492 Ricardo Wurmus 2023-12-15gnu: r-interactivedisplay: Add missing JavaScript sources....* gnu/packages/bioconductor.scm (r-interactivedisplay)[source]: Update list of minified JavaScript files to delete. [arguments]: Use minify-build-system to process JavaScript files. [native-inputs]: Add js-d3-v2; relabel js-datatables as js-datatables-1.10; add js-datatables-1.9. Change-Id: If8b6d9378680e9f69d71be56ce28add70b1a4564 Ricardo Wurmus 2023-12-15gnu: r-wgcna: Update to 1.72-5....* gnu/packages/bioconductor.scm (r-wgcna): Update to 1.72-5. Change-Id: Ib9d3b0e6ba191839e4b22cb0b8f63e6b0e9d2baa Ricardo Wurmus 2023-12-11gnu: Add r-polyester....* gnu/packages/bioconductor.scm (r-polyester): New variable. Change-Id: I415089cda3214ec04402fc6f4505fa868414d0e2 Signed-off-by: Ricardo Wurmus <rekado@elephly.net> Mădălin Ionel Patrașcu 2023-11-15gnu: r-abn: Update to 3.0.3....* gnu/packages/bioconductor.scm (r-abn): Update to 3.0.3. Change-Id: Id94ec47d805af9da63dbf43a9edbd5e7c3a5a62a Ricardo Wurmus 2023-11-15gnu: r-activepathways: Update to 2.0.3....* gnu/packages/bioconductor.scm (r-activepathways): Update to 2.0.3. Change-Id: I4199c307d48583c0894a39af78f6a4027cb0cf54 Ricardo Wurmus 2023-11-15gnu: r-ggpicrust2: Update to 1.7.3....* gnu/packages/bioconductor.scm (r-ggpicrust2): Update to 1.7.3. Change-Id: I18ab123d640ceec9315a21dff48ed2f1ea4ddff5 Ricardo Wurmus 2023-11-13gnu: Add r-bcrank....* gnu/packages/bioconductor.scm (r-bcrank): New variable. Change-Id: Id43ae3b7f6eff54e1180b489c90fc72176751f9f Mădălin Ionel Patrașcu