RemoteStore: Make thread-safe
This allows a RemoteStore object to be used safely from multiple threads concurrently. It will make multiple daemon connections if necessary. Note: pool.hh and sync.hh have been copied from the Hydra source tree.
This commit is contained in:
		
							parent
							
								
									c0b7a8a0b5
								
							
						
					
					
						commit
						e292144d46
					
				
					 4 changed files with 414 additions and 235 deletions
				
			
		|  | @ -6,6 +6,7 @@ | ||||||
| #include "affinity.hh" | #include "affinity.hh" | ||||||
| #include "globals.hh" | #include "globals.hh" | ||||||
| #include "derivations.hh" | #include "derivations.hh" | ||||||
|  | #include "pool.hh" | ||||||
| 
 | 
 | ||||||
| #include <sys/types.h> | #include <sys/types.h> | ||||||
| #include <sys/stat.h> | #include <sys/stat.h> | ||||||
|  | @ -14,7 +15,6 @@ | ||||||
| #include <errno.h> | #include <errno.h> | ||||||
| #include <fcntl.h> | #include <fcntl.h> | ||||||
| 
 | 
 | ||||||
| #include <iostream> |  | ||||||
| #include <unistd.h> | #include <unistd.h> | ||||||
| #include <cstring> | #include <cstring> | ||||||
| 
 | 
 | ||||||
|  | @ -40,61 +40,20 @@ template PathSet readStorePaths(Source & from); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| RemoteStore::RemoteStore() | RemoteStore::RemoteStore() | ||||||
|  |     : connections(make_ref<Pool<Connection>>([this]() { return openConnection(); })) | ||||||
| { | { | ||||||
|     initialised = false; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::openConnection(bool reserveSpace) | ref<RemoteStore::Connection> RemoteStore::openConnection(bool reserveSpace) | ||||||
| { | { | ||||||
|     if (initialised) return; |     auto conn = make_ref<Connection>(); | ||||||
|     initialised = true; |  | ||||||
| 
 | 
 | ||||||
|     /* Connect to a daemon that does the privileged work for us. */ |     /* Connect to a daemon that does the privileged work for us. */ | ||||||
|     connectToDaemon(); |     conn->fd = socket(PF_UNIX, SOCK_STREAM, 0); | ||||||
| 
 |     if (conn->fd == -1) | ||||||
|     from.fd = fdSocket; |  | ||||||
|     to.fd = fdSocket; |  | ||||||
| 
 |  | ||||||
|     /* Send the magic greeting, check for the reply. */ |  | ||||||
|     try { |  | ||||||
|         to << WORKER_MAGIC_1; |  | ||||||
|         to.flush(); |  | ||||||
|         unsigned int magic = readInt(from); |  | ||||||
|         if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch"); |  | ||||||
| 
 |  | ||||||
|         daemonVersion = readInt(from); |  | ||||||
|         if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) |  | ||||||
|             throw Error("Nix daemon protocol version not supported"); |  | ||||||
|         to << PROTOCOL_VERSION; |  | ||||||
| 
 |  | ||||||
|         if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) { |  | ||||||
|             int cpu = settings.lockCPU ? lockToCurrentCPU() : -1; |  | ||||||
|             if (cpu != -1) |  | ||||||
|                 to << 1 << cpu; |  | ||||||
|             else |  | ||||||
|                 to << 0; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) |  | ||||||
|             to << reserveSpace; |  | ||||||
| 
 |  | ||||||
|         processStderr(); |  | ||||||
|     } |  | ||||||
|     catch (Error & e) { |  | ||||||
|         throw Error(format("cannot start daemon worker: %1%") % e.msg()); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     setOptions(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| void RemoteStore::connectToDaemon() |  | ||||||
| { |  | ||||||
|     fdSocket = socket(PF_UNIX, SOCK_STREAM, 0); |  | ||||||
|     if (fdSocket == -1) |  | ||||||
|         throw SysError("cannot create Unix domain socket"); |         throw SysError("cannot create Unix domain socket"); | ||||||
|     closeOnExec(fdSocket); |     closeOnExec(conn->fd); | ||||||
| 
 | 
 | ||||||
|     string socketPath = settings.nixDaemonSocketFile; |     string socketPath = settings.nixDaemonSocketFile; | ||||||
| 
 | 
 | ||||||
|  | @ -111,111 +70,147 @@ void RemoteStore::connectToDaemon() | ||||||
|     addr.sun_family = AF_UNIX; |     addr.sun_family = AF_UNIX; | ||||||
|     if (socketPathRel.size() >= sizeof(addr.sun_path)) |     if (socketPathRel.size() >= sizeof(addr.sun_path)) | ||||||
|         throw Error(format("socket path ‘%1%’ is too long") % socketPathRel); |         throw Error(format("socket path ‘%1%’ is too long") % socketPathRel); | ||||||
|     using namespace std; |  | ||||||
|     strcpy(addr.sun_path, socketPathRel.c_str()); |     strcpy(addr.sun_path, socketPathRel.c_str()); | ||||||
| 
 | 
 | ||||||
|     if (connect(fdSocket, (struct sockaddr *) &addr, sizeof(addr)) == -1) |     if (connect(conn->fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) | ||||||
|         throw SysError(format("cannot connect to daemon at ‘%1%’") % socketPath); |         throw SysError(format("cannot connect to daemon at ‘%1%’") % socketPath); | ||||||
| 
 | 
 | ||||||
|     if (fchdir(fdPrevDir) == -1) |     if (fchdir(fdPrevDir) == -1) | ||||||
|         throw SysError("couldn't change back to previous directory"); |         throw SysError("couldn't change back to previous directory"); | ||||||
|  | 
 | ||||||
|  |     conn->from.fd = conn->fd; | ||||||
|  |     conn->to.fd = conn->fd; | ||||||
|  | 
 | ||||||
|  |     /* Send the magic greeting, check for the reply. */ | ||||||
|  |     try { | ||||||
|  |         conn->to << WORKER_MAGIC_1; | ||||||
|  |         conn->to.flush(); | ||||||
|  |         unsigned int magic = readInt(conn->from); | ||||||
|  |         if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch"); | ||||||
|  | 
 | ||||||
|  |         conn->daemonVersion = readInt(conn->from); | ||||||
|  |         if (GET_PROTOCOL_MAJOR(conn->daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) | ||||||
|  |             throw Error("Nix daemon protocol version not supported"); | ||||||
|  |         conn->to << PROTOCOL_VERSION; | ||||||
|  | 
 | ||||||
|  |         if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 14) { | ||||||
|  |             int cpu = settings.lockCPU ? lockToCurrentCPU() : -1; | ||||||
|  |             if (cpu != -1) | ||||||
|  |                 conn->to << 1 << cpu; | ||||||
|  |             else | ||||||
|  |                 conn->to << 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 11) | ||||||
|  |             conn->to << reserveSpace; | ||||||
|  | 
 | ||||||
|  |         conn->processStderr(); | ||||||
|  |     } | ||||||
|  |     catch (Error & e) { | ||||||
|  |         throw Error(format("cannot start daemon worker: %1%") % e.msg()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     setOptions(conn); | ||||||
|  | 
 | ||||||
|  |     return conn; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| RemoteStore::~RemoteStore() | RemoteStore::~RemoteStore() | ||||||
| { | { | ||||||
|     try { |     try { | ||||||
|         to.flush(); |         //to.flush();
 | ||||||
|         fdSocket.close(); |         //fdSocket.close();
 | ||||||
|  |         // FIXME: close pool
 | ||||||
|     } catch (...) { |     } catch (...) { | ||||||
|         ignoreException(); |         ignoreException(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::setOptions() | void RemoteStore::setOptions(ref<Connection> conn) | ||||||
| { | { | ||||||
|     to << wopSetOptions |     conn->to << wopSetOptions | ||||||
|        << settings.keepFailed |        << settings.keepFailed | ||||||
|        << settings.keepGoing |        << settings.keepGoing | ||||||
|        << settings.tryFallback |        << settings.tryFallback | ||||||
|        << verbosity |        << verbosity | ||||||
|        << settings.maxBuildJobs |        << settings.maxBuildJobs | ||||||
|        << settings.maxSilentTime; |        << settings.maxSilentTime; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 2) |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 2) | ||||||
|         to << settings.useBuildHook; |         conn->to << settings.useBuildHook; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 4) | ||||||
|         to << settings.buildVerbosity |         conn->to << settings.buildVerbosity | ||||||
|            << logType |            << logType | ||||||
|            << settings.printBuildTrace; |            << settings.printBuildTrace; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 6) |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 6) | ||||||
|         to << settings.buildCores; |         conn->to << settings.buildCores; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 10) |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 10) | ||||||
|         to << settings.useSubstitutes; |         conn->to << settings.useSubstitutes; | ||||||
| 
 | 
 | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 12) { | ||||||
|         Settings::SettingsMap overrides = settings.getOverrides(); |         Settings::SettingsMap overrides = settings.getOverrides(); | ||||||
|         if (overrides["ssh-auth-sock"] == "") |         if (overrides["ssh-auth-sock"] == "") | ||||||
|             overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK"); |             overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK"); | ||||||
|         to << overrides.size(); |         conn->to << overrides.size(); | ||||||
|         for (auto & i : overrides) |         for (auto & i : overrides) | ||||||
|             to << i.first << i.second; |             conn->to << i.first << i.second; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| bool RemoteStore::isValidPath(const Path & path) | bool RemoteStore::isValidPath(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopIsValidPath << path; |     conn->to << wopIsValidPath << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     unsigned int reply = readInt(from); |     unsigned int reply = readInt(conn->from); | ||||||
|     return reply != 0; |     return reply != 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryValidPaths(const PathSet & paths) | PathSet RemoteStore::queryValidPaths(const PathSet & paths) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 12) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { | ||||||
|         PathSet res; |         PathSet res; | ||||||
|         for (auto & i : paths) |         for (auto & i : paths) | ||||||
|             if (isValidPath(i)) res.insert(i); |             if (isValidPath(i)) res.insert(i); | ||||||
|         return res; |         return res; | ||||||
|     } else { |     } else { | ||||||
|         to << wopQueryValidPaths << paths; |         conn->to << wopQueryValidPaths << paths; | ||||||
|         processStderr(); |         conn->processStderr(); | ||||||
|         return readStorePaths<PathSet>(from); |         return readStorePaths<PathSet>(conn->from); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryAllValidPaths() | PathSet RemoteStore::queryAllValidPaths() | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryAllValidPaths; |     conn->to << wopQueryAllValidPaths; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStorePaths<PathSet>(from); |     return readStorePaths<PathSet>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths) | PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 12) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { | ||||||
|         PathSet res; |         PathSet res; | ||||||
|         for (auto & i : paths) { |         for (auto & i : paths) { | ||||||
|             to << wopHasSubstitutes << i; |             conn->to << wopHasSubstitutes << i; | ||||||
|             processStderr(); |             conn->processStderr(); | ||||||
|             if (readInt(from)) res.insert(i); |             if (readInt(conn->from)) res.insert(i); | ||||||
|         } |         } | ||||||
|         return res; |         return res; | ||||||
|     } else { |     } else { | ||||||
|         to << wopQuerySubstitutablePaths << paths; |         conn->to << wopQuerySubstitutablePaths << paths; | ||||||
|         processStderr(); |         conn->processStderr(); | ||||||
|         return readStorePaths<PathSet>(from); |         return readStorePaths<PathSet>(conn->from); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -225,39 +220,39 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
| { | { | ||||||
|     if (paths.empty()) return; |     if (paths.empty()) return; | ||||||
| 
 | 
 | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
| 
 | 
 | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return; |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 3) return; | ||||||
| 
 | 
 | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 12) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { | ||||||
| 
 | 
 | ||||||
|         for (auto & i : paths) { |         for (auto & i : paths) { | ||||||
|             SubstitutablePathInfo info; |             SubstitutablePathInfo info; | ||||||
|             to << wopQuerySubstitutablePathInfo << i; |             conn->to << wopQuerySubstitutablePathInfo << i; | ||||||
|             processStderr(); |             conn->processStderr(); | ||||||
|             unsigned int reply = readInt(from); |             unsigned int reply = readInt(conn->from); | ||||||
|             if (reply == 0) continue; |             if (reply == 0) continue; | ||||||
|             info.deriver = readString(from); |             info.deriver = readString(conn->from); | ||||||
|             if (info.deriver != "") assertStorePath(info.deriver); |             if (info.deriver != "") assertStorePath(info.deriver); | ||||||
|             info.references = readStorePaths<PathSet>(from); |             info.references = readStorePaths<PathSet>(conn->from); | ||||||
|             info.downloadSize = readLongLong(from); |             info.downloadSize = readLongLong(conn->from); | ||||||
|             info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0; |             info.narSize = GET_PROTOCOL_MINOR(conn->daemonVersion) >= 7 ? readLongLong(conn->from) : 0; | ||||||
|             infos[i] = info; |             infos[i] = info; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } else { |     } else { | ||||||
| 
 | 
 | ||||||
|         to << wopQuerySubstitutablePathInfos << paths; |         conn->to << wopQuerySubstitutablePathInfos << paths; | ||||||
|         processStderr(); |         conn->processStderr(); | ||||||
|         unsigned int count = readInt(from); |         unsigned int count = readInt(conn->from); | ||||||
|         for (unsigned int n = 0; n < count; n++) { |         for (unsigned int n = 0; n < count; n++) { | ||||||
|             Path path = readStorePath(from); |             Path path = readStorePath(conn->from); | ||||||
|             SubstitutablePathInfo & info(infos[path]); |             SubstitutablePathInfo & info(infos[path]); | ||||||
|             info.deriver = readString(from); |             info.deriver = readString(conn->from); | ||||||
|             if (info.deriver != "") assertStorePath(info.deriver); |             if (info.deriver != "") assertStorePath(info.deriver); | ||||||
|             info.references = readStorePaths<PathSet>(from); |             info.references = readStorePaths<PathSet>(conn->from); | ||||||
|             info.downloadSize = readLongLong(from); |             info.downloadSize = readLongLong(conn->from); | ||||||
|             info.narSize = readLongLong(from); |             info.narSize = readLongLong(conn->from); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | @ -266,27 +261,27 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths, | ||||||
| 
 | 
 | ||||||
| ValidPathInfo RemoteStore::queryPathInfo(const Path & path) | ValidPathInfo RemoteStore::queryPathInfo(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryPathInfo << path; |     conn->to << wopQueryPathInfo << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     ValidPathInfo info; |     ValidPathInfo info; | ||||||
|     info.path = path; |     info.path = path; | ||||||
|     info.deriver = readString(from); |     info.deriver = readString(conn->from); | ||||||
|     if (info.deriver != "") assertStorePath(info.deriver); |     if (info.deriver != "") assertStorePath(info.deriver); | ||||||
|     info.narHash = parseHash(htSHA256, readString(from)); |     info.narHash = parseHash(htSHA256, readString(conn->from)); | ||||||
|     info.references = readStorePaths<PathSet>(from); |     info.references = readStorePaths<PathSet>(conn->from); | ||||||
|     info.registrationTime = readInt(from); |     info.registrationTime = readInt(conn->from); | ||||||
|     info.narSize = readLongLong(from); |     info.narSize = readLongLong(conn->from); | ||||||
|     return info; |     return info; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Hash RemoteStore::queryPathHash(const Path & path) | Hash RemoteStore::queryPathHash(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryPathHash << path; |     conn->to << wopQueryPathHash << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     string hash = readString(from); |     string hash = readString(conn->from); | ||||||
|     return parseHash(htSHA256, hash); |     return parseHash(htSHA256, hash); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -294,10 +289,10 @@ Hash RemoteStore::queryPathHash(const Path & path) | ||||||
| void RemoteStore::queryReferences(const Path & path, | void RemoteStore::queryReferences(const Path & path, | ||||||
|     PathSet & references) |     PathSet & references) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryReferences << path; |     conn->to << wopQueryReferences << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     PathSet references2 = readStorePaths<PathSet>(from); |     PathSet references2 = readStorePaths<PathSet>(conn->from); | ||||||
|     references.insert(references2.begin(), references2.end()); |     references.insert(references2.begin(), references2.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -305,20 +300,20 @@ void RemoteStore::queryReferences(const Path & path, | ||||||
| void RemoteStore::queryReferrers(const Path & path, | void RemoteStore::queryReferrers(const Path & path, | ||||||
|     PathSet & referrers) |     PathSet & referrers) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryReferrers << path; |     conn->to << wopQueryReferrers << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     PathSet referrers2 = readStorePaths<PathSet>(from); |     PathSet referrers2 = readStorePaths<PathSet>(conn->from); | ||||||
|     referrers.insert(referrers2.begin(), referrers2.end()); |     referrers.insert(referrers2.begin(), referrers2.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Path RemoteStore::queryDeriver(const Path & path) | Path RemoteStore::queryDeriver(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryDeriver << path; |     conn->to << wopQueryDeriver << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     Path drvPath = readString(from); |     Path drvPath = readString(conn->from); | ||||||
|     if (drvPath != "") assertStorePath(drvPath); |     if (drvPath != "") assertStorePath(drvPath); | ||||||
|     return drvPath; |     return drvPath; | ||||||
| } | } | ||||||
|  | @ -326,37 +321,37 @@ Path RemoteStore::queryDeriver(const Path & path) | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryValidDerivers(const Path & path) | PathSet RemoteStore::queryValidDerivers(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryValidDerivers << path; |     conn->to << wopQueryValidDerivers << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStorePaths<PathSet>(from); |     return readStorePaths<PathSet>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryDerivationOutputs(const Path & path) | PathSet RemoteStore::queryDerivationOutputs(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryDerivationOutputs << path; |     conn->to << wopQueryDerivationOutputs << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStorePaths<PathSet>(from); |     return readStorePaths<PathSet>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryDerivationOutputNames(const Path & path) | PathSet RemoteStore::queryDerivationOutputNames(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryDerivationOutputNames << path; |     conn->to << wopQueryDerivationOutputNames << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStrings<PathSet>(from); |     return readStrings<PathSet>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Path RemoteStore::queryPathFromHashPart(const string & hashPart) | Path RemoteStore::queryPathFromHashPart(const string & hashPart) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryPathFromHashPart << hashPart; |     conn->to << wopQueryPathFromHashPart << hashPart; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     Path path = readString(from); |     Path path = readString(conn->from); | ||||||
|     if (!path.empty()) assertStorePath(path); |     if (!path.empty()) assertStorePath(path); | ||||||
|     return path; |     return path; | ||||||
| } | } | ||||||
|  | @ -367,32 +362,32 @@ Path RemoteStore::addToStore(const string & name, const Path & _srcPath, | ||||||
| { | { | ||||||
|     if (repair) throw Error("repairing is not supported when building through the Nix daemon"); |     if (repair) throw Error("repairing is not supported when building through the Nix daemon"); | ||||||
| 
 | 
 | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
| 
 | 
 | ||||||
|     Path srcPath(absPath(_srcPath)); |     Path srcPath(absPath(_srcPath)); | ||||||
| 
 | 
 | ||||||
|     to << wopAddToStore << name |     conn->to << wopAddToStore << name | ||||||
|        << ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */ |        << ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */ | ||||||
|        << (recursive ? 1 : 0) |        << (recursive ? 1 : 0) | ||||||
|        << printHashType(hashAlgo); |        << printHashType(hashAlgo); | ||||||
| 
 | 
 | ||||||
|     try { |     try { | ||||||
|         to.written = 0; |         conn->to.written = 0; | ||||||
|         to.warn = true; |         conn->to.warn = true; | ||||||
|         dumpPath(srcPath, to, filter); |         dumpPath(srcPath, conn->to, filter); | ||||||
|         to.warn = false; |         conn->to.warn = false; | ||||||
|         processStderr(); |         conn->processStderr(); | ||||||
|     } catch (SysError & e) { |     } catch (SysError & e) { | ||||||
|         /* Daemon closed while we were sending the path. Probably OOM
 |         /* Daemon closed while we were sending the path. Probably OOM
 | ||||||
|            or I/O error. */ |            or I/O error. */ | ||||||
|         if (e.errNo == EPIPE) |         if (e.errNo == EPIPE) | ||||||
|             try { |             try { | ||||||
|                 processStderr(); |                 conn->processStderr(); | ||||||
|             } catch (EndOfFile & e) { } |             } catch (EndOfFile & e) { } | ||||||
|         throw; |         throw; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return readStorePath(from); |     return readStorePath(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -401,43 +396,43 @@ Path RemoteStore::addTextToStore(const string & name, const string & s, | ||||||
| { | { | ||||||
|     if (repair) throw Error("repairing is not supported when building through the Nix daemon"); |     if (repair) throw Error("repairing is not supported when building through the Nix daemon"); | ||||||
| 
 | 
 | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopAddTextToStore << name << s << references; |     conn->to << wopAddTextToStore << name << s << references; | ||||||
| 
 | 
 | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStorePath(from); |     return readStorePath(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::exportPath(const Path & path, bool sign, | void RemoteStore::exportPath(const Path & path, bool sign, | ||||||
|     Sink & sink) |     Sink & sink) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopExportPath << path << (sign ? 1 : 0); |     conn->to << wopExportPath << path << (sign ? 1 : 0); | ||||||
|     processStderr(&sink); /* sink receives the actual data */ |     conn->processStderr(&sink); /* sink receives the actual data */ | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Paths RemoteStore::importPaths(bool requireSignature, Source & source) | Paths RemoteStore::importPaths(bool requireSignature, Source & source) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopImportPaths; |     conn->to << wopImportPaths; | ||||||
|     /* We ignore requireSignature, since the worker forces it to true
 |     /* We ignore requireSignature, since the worker forces it to true
 | ||||||
|        anyway. */ |        anyway. */ | ||||||
|     processStderr(0, &source); |     conn->processStderr(0, &source); | ||||||
|     return readStorePaths<Paths>(from); |     return readStorePaths<Paths>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopBuildPaths; |     conn->to << wopBuildPaths; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 13) { |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13) { | ||||||
|         to << drvPaths; |         conn->to << drvPaths; | ||||||
|         if (GET_PROTOCOL_MINOR(daemonVersion) >= 15) |         if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 15) | ||||||
|             to << buildMode; |             conn->to << buildMode; | ||||||
|         else |         else | ||||||
|             /* Old daemons did not take a 'buildMode' parameter, so we
 |             /* Old daemons did not take a 'buildMode' parameter, so we
 | ||||||
|                need to validate it here on the client side.  */ |                need to validate it here on the client side.  */ | ||||||
|  | @ -449,22 +444,22 @@ void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | ||||||
|         PathSet drvPaths2; |         PathSet drvPaths2; | ||||||
|         for (auto & i : drvPaths) |         for (auto & i : drvPaths) | ||||||
|             drvPaths2.insert(string(i, 0, i.find('!'))); |             drvPaths2.insert(string(i, 0, i.find('!'))); | ||||||
|         to << drvPaths2; |         conn->to << drvPaths2; | ||||||
|     } |     } | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDerivation & drv, | BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDerivation & drv, | ||||||
|     BuildMode buildMode) |     BuildMode buildMode) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopBuildDerivation << drvPath << drv << buildMode; |     conn->to << wopBuildDerivation << drvPath << drv << buildMode; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     BuildResult res; |     BuildResult res; | ||||||
|     unsigned int status; |     unsigned int status; | ||||||
|     from >> status >> res.errorMsg; |     conn->from >> status >> res.errorMsg; | ||||||
|     res.status = (BuildResult::Status) status; |     res.status = (BuildResult::Status) status; | ||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
|  | @ -472,50 +467,50 @@ BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDeriva | ||||||
| 
 | 
 | ||||||
| void RemoteStore::ensurePath(const Path & path) | void RemoteStore::ensurePath(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopEnsurePath << path; |     conn->to << wopEnsurePath << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::addTempRoot(const Path & path) | void RemoteStore::addTempRoot(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopAddTempRoot << path; |     conn->to << wopAddTempRoot << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::addIndirectRoot(const Path & path) | void RemoteStore::addIndirectRoot(const Path & path) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopAddIndirectRoot << path; |     conn->to << wopAddIndirectRoot << path; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::syncWithGC() | void RemoteStore::syncWithGC() | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopSyncWithGC; |     conn->to << wopSyncWithGC; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Roots RemoteStore::findRoots() | Roots RemoteStore::findRoots() | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopFindRoots; |     conn->to << wopFindRoots; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     unsigned int count = readInt(from); |     unsigned int count = readInt(conn->from); | ||||||
|     Roots result; |     Roots result; | ||||||
|     while (count--) { |     while (count--) { | ||||||
|         Path link = readString(from); |         Path link = readString(conn->from); | ||||||
|         Path target = readStorePath(from); |         Path target = readStorePath(conn->from); | ||||||
|         result[link] = target; |         result[link] = target; | ||||||
|     } |     } | ||||||
|     return result; |     return result; | ||||||
|  | @ -524,56 +519,56 @@ Roots RemoteStore::findRoots() | ||||||
| 
 | 
 | ||||||
| void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) | void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results) | ||||||
| { | { | ||||||
|     openConnection(false); |     auto conn(connections->get()); | ||||||
| 
 | 
 | ||||||
|     to << wopCollectGarbage << options.action << options.pathsToDelete << options.ignoreLiveness |     conn->to << wopCollectGarbage << options.action << options.pathsToDelete << options.ignoreLiveness | ||||||
|        << options.maxFreed << 0; |        << options.maxFreed << 0; | ||||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) |     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 5) | ||||||
|         /* removed options */ |         /* removed options */ | ||||||
|         to << 0 << 0; |         conn->to << 0 << 0; | ||||||
| 
 | 
 | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
| 
 | 
 | ||||||
|     results.paths = readStrings<PathSet>(from); |     results.paths = readStrings<PathSet>(conn->from); | ||||||
|     results.bytesFreed = readLongLong(from); |     results.bytesFreed = readLongLong(conn->from); | ||||||
|     readLongLong(from); // obsolete
 |     readLongLong(conn->from); // obsolete
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| PathSet RemoteStore::queryFailedPaths() | PathSet RemoteStore::queryFailedPaths() | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopQueryFailedPaths; |     conn->to << wopQueryFailedPaths; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readStorePaths<PathSet>(from); |     return readStorePaths<PathSet>(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| void RemoteStore::clearFailedPaths(const PathSet & paths) | void RemoteStore::clearFailedPaths(const PathSet & paths) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopClearFailedPaths << paths; |     conn->to << wopClearFailedPaths << paths; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RemoteStore::optimiseStore() | void RemoteStore::optimiseStore() | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopOptimiseStore; |     conn->to << wopOptimiseStore; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     readInt(from); |     readInt(conn->from); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool RemoteStore::verifyStore(bool checkContents, bool repair) | bool RemoteStore::verifyStore(bool checkContents, bool repair) | ||||||
| { | { | ||||||
|     openConnection(); |     auto conn(connections->get()); | ||||||
|     to << wopVerifyStore << checkContents << repair; |     conn->to << wopVerifyStore << checkContents << repair; | ||||||
|     processStderr(); |     conn->processStderr(); | ||||||
|     return readInt(from) != 0; |     return readInt(conn->from) != 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RemoteStore::processStderr(Sink * sink, Source * source) | void RemoteStore::Connection::processStderr(Sink * sink, Source * source) | ||||||
| { | { | ||||||
|     to.flush(); |     to.flush(); | ||||||
|     unsigned int msg; |     unsigned int msg; | ||||||
|  |  | ||||||
|  | @ -12,6 +12,7 @@ class Pipe; | ||||||
| class Pid; | class Pid; | ||||||
| struct FdSink; | struct FdSink; | ||||||
| struct FdSource; | struct FdSource; | ||||||
|  | template<typename T> class Pool; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class RemoteStore : public Store | class RemoteStore : public Store | ||||||
|  | @ -91,19 +92,22 @@ public: | ||||||
|     bool verifyStore(bool checkContents, bool repair) override; |     bool verifyStore(bool checkContents, bool repair) override; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     AutoCloseFD fdSocket; |  | ||||||
|     FdSink to; |  | ||||||
|     FdSource from; |  | ||||||
|     unsigned int daemonVersion; |  | ||||||
|     bool initialised; |  | ||||||
| 
 | 
 | ||||||
|     void openConnection(bool reserveSpace = true); |     struct Connection | ||||||
|  |     { | ||||||
|  |         AutoCloseFD fd; | ||||||
|  |         FdSink to; | ||||||
|  |         FdSource from; | ||||||
|  |         unsigned int daemonVersion; | ||||||
| 
 | 
 | ||||||
|     void processStderr(Sink * sink = 0, Source * source = 0); |         void processStderr(Sink * sink = 0, Source * source = 0); | ||||||
|  |     }; | ||||||
| 
 | 
 | ||||||
|     void connectToDaemon(); |     ref<Pool<Connection>> connections; | ||||||
| 
 | 
 | ||||||
|     void setOptions(); |     ref<Connection> openConnection(bool reserveSpace = true); | ||||||
|  | 
 | ||||||
|  |     void setOptions(ref<Connection> conn); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										102
									
								
								src/libutil/pool.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/libutil/pool.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,102 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | #include <list> | ||||||
|  | #include <functional> | ||||||
|  | 
 | ||||||
|  | #include "sync.hh" | ||||||
|  | #include "ref.hh" | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | /* This template class implements a simple pool manager of resources
 | ||||||
|  |    of some type R, such as database connections. It is used as | ||||||
|  |    follows: | ||||||
|  | 
 | ||||||
|  |      class Connection { ... }; | ||||||
|  | 
 | ||||||
|  |      Pool<Connection> pool; | ||||||
|  | 
 | ||||||
|  |      { | ||||||
|  |        auto conn(pool.get()); | ||||||
|  |        conn->exec("select ..."); | ||||||
|  |      } | ||||||
|  | 
 | ||||||
|  |    Here, the Connection object referenced by ‘conn’ is automatically | ||||||
|  |    returned to the pool when ‘conn’ goes out of scope. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | template <class R> | ||||||
|  | class Pool | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     typedef std::function<ref<R>()> Factory; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 
 | ||||||
|  |     Factory factory; | ||||||
|  | 
 | ||||||
|  |     struct State | ||||||
|  |     { | ||||||
|  |         unsigned int count = 0; | ||||||
|  |         std::list<ref<R>> idle; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Sync<State> state; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     Pool(const Factory & factory = []() { return make_ref<R>(); }) | ||||||
|  |         : factory(factory) | ||||||
|  |     { } | ||||||
|  | 
 | ||||||
|  |     class Handle | ||||||
|  |     { | ||||||
|  |     private: | ||||||
|  |         Pool & pool; | ||||||
|  |         ref<R> r; | ||||||
|  | 
 | ||||||
|  |         friend Pool; | ||||||
|  | 
 | ||||||
|  |         Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { } | ||||||
|  | 
 | ||||||
|  |     public: | ||||||
|  |         Handle(Handle && h) : pool(h.pool), r(h.r) { abort(); } | ||||||
|  | 
 | ||||||
|  |         Handle(const Handle & l) = delete; | ||||||
|  | 
 | ||||||
|  |         ~Handle() | ||||||
|  |         { | ||||||
|  |             auto state_(pool.state.lock()); | ||||||
|  |             state_->idle.push_back(r); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         R * operator -> () { return &*r; } | ||||||
|  |         R & operator * () { return *r; } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Handle get() | ||||||
|  |     { | ||||||
|  |         { | ||||||
|  |             auto state_(state.lock()); | ||||||
|  |             if (!state_->idle.empty()) { | ||||||
|  |                 auto p = state_->idle.back(); | ||||||
|  |                 state_->idle.pop_back(); | ||||||
|  |                 return Handle(*this, p); | ||||||
|  |             } | ||||||
|  |             state_->count++; | ||||||
|  |         } | ||||||
|  |         /* Note: we don't hold the lock while creating a new instance,
 | ||||||
|  |            because creation might take a long time. */ | ||||||
|  |         return Handle(*this, factory()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsigned int count() | ||||||
|  |     { | ||||||
|  |         auto state_(state.lock()); | ||||||
|  |         return state_->count; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								src/libutil/sync.hh
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/libutil/sync.hh
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <mutex> | ||||||
|  | #include <condition_variable> | ||||||
|  | #include <cassert> | ||||||
|  | 
 | ||||||
|  | namespace nix { | ||||||
|  | 
 | ||||||
|  | /* This template class ensures synchronized access to a value of type
 | ||||||
|  |    T. It is used as follows: | ||||||
|  | 
 | ||||||
|  |      struct Data { int x; ... }; | ||||||
|  | 
 | ||||||
|  |      Sync<Data> data; | ||||||
|  | 
 | ||||||
|  |      { | ||||||
|  |        auto data_(data.lock()); | ||||||
|  |        data_->x = 123; | ||||||
|  |      } | ||||||
|  | 
 | ||||||
|  |    Here, "data" is automatically unlocked when "data_" goes out of | ||||||
|  |    scope. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | template<class T> | ||||||
|  | class Sync | ||||||
|  | { | ||||||
|  | private: | ||||||
|  |     std::mutex mutex; | ||||||
|  |     T data; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  | 
 | ||||||
|  |     Sync() { } | ||||||
|  |     Sync(const T & data) : data(data) { } | ||||||
|  | 
 | ||||||
|  |     class Lock | ||||||
|  |     { | ||||||
|  |     private: | ||||||
|  |         Sync * s; | ||||||
|  |         friend Sync; | ||||||
|  |         Lock(Sync * s) : s(s) { s->mutex.lock(); } | ||||||
|  |     public: | ||||||
|  |         Lock(Lock && l) : s(l.s) { l.s = 0; } | ||||||
|  |         Lock(const Lock & l) = delete; | ||||||
|  |         ~Lock() { if (s) s->mutex.unlock(); } | ||||||
|  |         T * operator -> () { return &s->data; } | ||||||
|  |         T & operator * () { return s->data; } | ||||||
|  | 
 | ||||||
|  |         /* FIXME: performance impact of condition_variable_any? */ | ||||||
|  |         void wait(std::condition_variable_any & cv) | ||||||
|  |         { | ||||||
|  |             assert(s); | ||||||
|  |             cv.wait(s->mutex); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         template<class Rep, class Period, class Predicate> | ||||||
|  |         bool wait_for(std::condition_variable_any & cv, | ||||||
|  |             const std::chrono::duration<Rep, Period> & duration, | ||||||
|  |             Predicate pred) | ||||||
|  |         { | ||||||
|  |             assert(s); | ||||||
|  |             return cv.wait_for(s->mutex, duration, pred); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         template<class Clock, class Duration> | ||||||
|  |         std::cv_status wait_until(std::condition_variable_any & cv, | ||||||
|  |             const std::chrono::time_point<Clock, Duration> & duration) | ||||||
|  |         { | ||||||
|  |             assert(s); | ||||||
|  |             return cv.wait_until(s->mutex, duration); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     Lock lock() { return Lock(this); } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue