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 "globals.hh" | ||||
| #include "derivations.hh" | ||||
| #include "pool.hh" | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
|  | @ -14,7 +15,6 @@ | |||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <unistd.h> | ||||
| #include <cstring> | ||||
| 
 | ||||
|  | @ -40,61 +40,20 @@ template PathSet readStorePaths(Source & from); | |||
| 
 | ||||
| 
 | ||||
| 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; | ||||
|     initialised = true; | ||||
|     auto conn = make_ref<Connection>(); | ||||
| 
 | ||||
|     /* Connect to a daemon that does the privileged work for us. */ | ||||
|     connectToDaemon(); | ||||
| 
 | ||||
|     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) | ||||
|     conn->fd = socket(PF_UNIX, SOCK_STREAM, 0); | ||||
|     if (conn->fd == -1) | ||||
|         throw SysError("cannot create Unix domain socket"); | ||||
|     closeOnExec(fdSocket); | ||||
|     closeOnExec(conn->fd); | ||||
| 
 | ||||
|     string socketPath = settings.nixDaemonSocketFile; | ||||
| 
 | ||||
|  | @ -111,111 +70,147 @@ void RemoteStore::connectToDaemon() | |||
|     addr.sun_family = AF_UNIX; | ||||
|     if (socketPathRel.size() >= sizeof(addr.sun_path)) | ||||
|         throw Error(format("socket path ‘%1%’ is too long") % socketPathRel); | ||||
|     using namespace std; | ||||
|     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); | ||||
| 
 | ||||
|     if (fchdir(fdPrevDir) == -1) | ||||
|         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() | ||||
| { | ||||
|     try { | ||||
|         to.flush(); | ||||
|         fdSocket.close(); | ||||
|         //to.flush();
 | ||||
|         //fdSocket.close();
 | ||||
|         // FIXME: close pool
 | ||||
|     } catch (...) { | ||||
|         ignoreException(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::setOptions() | ||||
| void RemoteStore::setOptions(ref<Connection> conn) | ||||
| { | ||||
|     to << wopSetOptions | ||||
|     conn->to << wopSetOptions | ||||
|        << settings.keepFailed | ||||
|        << settings.keepGoing | ||||
|        << settings.tryFallback | ||||
|        << verbosity | ||||
|        << settings.maxBuildJobs | ||||
|        << settings.maxSilentTime; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 2) | ||||
|         to << settings.useBuildHook; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) | ||||
|         to << settings.buildVerbosity | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 2) | ||||
|         conn->to << settings.useBuildHook; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 4) | ||||
|         conn->to << settings.buildVerbosity | ||||
|            << logType | ||||
|            << settings.printBuildTrace; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 6) | ||||
|         to << settings.buildCores; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 10) | ||||
|         to << settings.useSubstitutes; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 6) | ||||
|         conn->to << settings.buildCores; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 10) | ||||
|         conn->to << settings.useSubstitutes; | ||||
| 
 | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) { | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 12) { | ||||
|         Settings::SettingsMap overrides = settings.getOverrides(); | ||||
|         if (overrides["ssh-auth-sock"] == "") | ||||
|             overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK"); | ||||
|         to << overrides.size(); | ||||
|         conn->to << overrides.size(); | ||||
|         for (auto & i : overrides) | ||||
|             to << i.first << i.second; | ||||
|             conn->to << i.first << i.second; | ||||
|     } | ||||
| 
 | ||||
|     processStderr(); | ||||
|     conn->processStderr(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool RemoteStore::isValidPath(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopIsValidPath << path; | ||||
|     processStderr(); | ||||
|     unsigned int reply = readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopIsValidPath << path; | ||||
|     conn->processStderr(); | ||||
|     unsigned int reply = readInt(conn->from); | ||||
|     return reply != 0; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::queryValidPaths(const PathSet & paths) | ||||
| { | ||||
|     openConnection(); | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 12) { | ||||
|     auto conn(connections->get()); | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { | ||||
|         PathSet res; | ||||
|         for (auto & i : paths) | ||||
|             if (isValidPath(i)) res.insert(i); | ||||
|         return res; | ||||
|     } else { | ||||
|         to << wopQueryValidPaths << paths; | ||||
|         processStderr(); | ||||
|         return readStorePaths<PathSet>(from); | ||||
|         conn->to << wopQueryValidPaths << paths; | ||||
|         conn->processStderr(); | ||||
|         return readStorePaths<PathSet>(conn->from); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::queryAllValidPaths() | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryAllValidPaths; | ||||
|     processStderr(); | ||||
|     return readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryAllValidPaths; | ||||
|     conn->processStderr(); | ||||
|     return readStorePaths<PathSet>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths) | ||||
| { | ||||
|     openConnection(); | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) < 12) { | ||||
|     auto conn(connections->get()); | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { | ||||
|         PathSet res; | ||||
|         for (auto & i : paths) { | ||||
|             to << wopHasSubstitutes << i; | ||||
|             processStderr(); | ||||
|             if (readInt(from)) res.insert(i); | ||||
|             conn->to << wopHasSubstitutes << i; | ||||
|             conn->processStderr(); | ||||
|             if (readInt(conn->from)) res.insert(i); | ||||
|         } | ||||
|         return res; | ||||
|     } else { | ||||
|         to << wopQuerySubstitutablePaths << paths; | ||||
|         processStderr(); | ||||
|         return readStorePaths<PathSet>(from); | ||||
|         conn->to << wopQuerySubstitutablePaths << paths; | ||||
|         conn->processStderr(); | ||||
|         return readStorePaths<PathSet>(conn->from); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -225,39 +220,39 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths, | |||
| { | ||||
|     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) { | ||||
|             SubstitutablePathInfo info; | ||||
|             to << wopQuerySubstitutablePathInfo << i; | ||||
|             processStderr(); | ||||
|             unsigned int reply = readInt(from); | ||||
|             conn->to << wopQuerySubstitutablePathInfo << i; | ||||
|             conn->processStderr(); | ||||
|             unsigned int reply = readInt(conn->from); | ||||
|             if (reply == 0) continue; | ||||
|             info.deriver = readString(from); | ||||
|             info.deriver = readString(conn->from); | ||||
|             if (info.deriver != "") assertStorePath(info.deriver); | ||||
|             info.references = readStorePaths<PathSet>(from); | ||||
|             info.downloadSize = readLongLong(from); | ||||
|             info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0; | ||||
|             info.references = readStorePaths<PathSet>(conn->from); | ||||
|             info.downloadSize = readLongLong(conn->from); | ||||
|             info.narSize = GET_PROTOCOL_MINOR(conn->daemonVersion) >= 7 ? readLongLong(conn->from) : 0; | ||||
|             infos[i] = info; | ||||
|         } | ||||
| 
 | ||||
|     } else { | ||||
| 
 | ||||
|         to << wopQuerySubstitutablePathInfos << paths; | ||||
|         processStderr(); | ||||
|         unsigned int count = readInt(from); | ||||
|         conn->to << wopQuerySubstitutablePathInfos << paths; | ||||
|         conn->processStderr(); | ||||
|         unsigned int count = readInt(conn->from); | ||||
|         for (unsigned int n = 0; n < count; n++) { | ||||
|             Path path = readStorePath(from); | ||||
|             Path path = readStorePath(conn->from); | ||||
|             SubstitutablePathInfo & info(infos[path]); | ||||
|             info.deriver = readString(from); | ||||
|             info.deriver = readString(conn->from); | ||||
|             if (info.deriver != "") assertStorePath(info.deriver); | ||||
|             info.references = readStorePaths<PathSet>(from); | ||||
|             info.downloadSize = readLongLong(from); | ||||
|             info.narSize = readLongLong(from); | ||||
|             info.references = readStorePaths<PathSet>(conn->from); | ||||
|             info.downloadSize = readLongLong(conn->from); | ||||
|             info.narSize = readLongLong(conn->from); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|  | @ -266,27 +261,27 @@ void RemoteStore::querySubstitutablePathInfos(const PathSet & paths, | |||
| 
 | ||||
| ValidPathInfo RemoteStore::queryPathInfo(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryPathInfo << path; | ||||
|     processStderr(); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryPathInfo << path; | ||||
|     conn->processStderr(); | ||||
|     ValidPathInfo info; | ||||
|     info.path = path; | ||||
|     info.deriver = readString(from); | ||||
|     info.deriver = readString(conn->from); | ||||
|     if (info.deriver != "") assertStorePath(info.deriver); | ||||
|     info.narHash = parseHash(htSHA256, readString(from)); | ||||
|     info.references = readStorePaths<PathSet>(from); | ||||
|     info.registrationTime = readInt(from); | ||||
|     info.narSize = readLongLong(from); | ||||
|     info.narHash = parseHash(htSHA256, readString(conn->from)); | ||||
|     info.references = readStorePaths<PathSet>(conn->from); | ||||
|     info.registrationTime = readInt(conn->from); | ||||
|     info.narSize = readLongLong(conn->from); | ||||
|     return info; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Hash RemoteStore::queryPathHash(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryPathHash << path; | ||||
|     processStderr(); | ||||
|     string hash = readString(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryPathHash << path; | ||||
|     conn->processStderr(); | ||||
|     string hash = readString(conn->from); | ||||
|     return parseHash(htSHA256, hash); | ||||
| } | ||||
| 
 | ||||
|  | @ -294,10 +289,10 @@ Hash RemoteStore::queryPathHash(const Path & path) | |||
| void RemoteStore::queryReferences(const Path & path, | ||||
|     PathSet & references) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryReferences << path; | ||||
|     processStderr(); | ||||
|     PathSet references2 = readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryReferences << path; | ||||
|     conn->processStderr(); | ||||
|     PathSet references2 = readStorePaths<PathSet>(conn->from); | ||||
|     references.insert(references2.begin(), references2.end()); | ||||
| } | ||||
| 
 | ||||
|  | @ -305,20 +300,20 @@ void RemoteStore::queryReferences(const Path & path, | |||
| void RemoteStore::queryReferrers(const Path & path, | ||||
|     PathSet & referrers) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryReferrers << path; | ||||
|     processStderr(); | ||||
|     PathSet referrers2 = readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryReferrers << path; | ||||
|     conn->processStderr(); | ||||
|     PathSet referrers2 = readStorePaths<PathSet>(conn->from); | ||||
|     referrers.insert(referrers2.begin(), referrers2.end()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Path RemoteStore::queryDeriver(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryDeriver << path; | ||||
|     processStderr(); | ||||
|     Path drvPath = readString(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryDeriver << path; | ||||
|     conn->processStderr(); | ||||
|     Path drvPath = readString(conn->from); | ||||
|     if (drvPath != "") assertStorePath(drvPath); | ||||
|     return drvPath; | ||||
| } | ||||
|  | @ -326,37 +321,37 @@ Path RemoteStore::queryDeriver(const Path & path) | |||
| 
 | ||||
| PathSet RemoteStore::queryValidDerivers(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryValidDerivers << path; | ||||
|     processStderr(); | ||||
|     return readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryValidDerivers << path; | ||||
|     conn->processStderr(); | ||||
|     return readStorePaths<PathSet>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::queryDerivationOutputs(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryDerivationOutputs << path; | ||||
|     processStderr(); | ||||
|     return readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryDerivationOutputs << path; | ||||
|     conn->processStderr(); | ||||
|     return readStorePaths<PathSet>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::queryDerivationOutputNames(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryDerivationOutputNames << path; | ||||
|     processStderr(); | ||||
|     return readStrings<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryDerivationOutputNames << path; | ||||
|     conn->processStderr(); | ||||
|     return readStrings<PathSet>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Path RemoteStore::queryPathFromHashPart(const string & hashPart) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryPathFromHashPart << hashPart; | ||||
|     processStderr(); | ||||
|     Path path = readString(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryPathFromHashPart << hashPart; | ||||
|     conn->processStderr(); | ||||
|     Path path = readString(conn->from); | ||||
|     if (!path.empty()) assertStorePath(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"); | ||||
| 
 | ||||
|     openConnection(); | ||||
|     auto conn(connections->get()); | ||||
| 
 | ||||
|     Path srcPath(absPath(_srcPath)); | ||||
| 
 | ||||
|     to << wopAddToStore << name | ||||
|     conn->to << wopAddToStore << name | ||||
|        << ((hashAlgo == htSHA256 && recursive) ? 0 : 1) /* backwards compatibility hack */ | ||||
|        << (recursive ? 1 : 0) | ||||
|        << printHashType(hashAlgo); | ||||
| 
 | ||||
|     try { | ||||
|         to.written = 0; | ||||
|         to.warn = true; | ||||
|         dumpPath(srcPath, to, filter); | ||||
|         to.warn = false; | ||||
|         processStderr(); | ||||
|         conn->to.written = 0; | ||||
|         conn->to.warn = true; | ||||
|         dumpPath(srcPath, conn->to, filter); | ||||
|         conn->to.warn = false; | ||||
|         conn->processStderr(); | ||||
|     } catch (SysError & e) { | ||||
|         /* Daemon closed while we were sending the path. Probably OOM
 | ||||
|            or I/O error. */ | ||||
|         if (e.errNo == EPIPE) | ||||
|             try { | ||||
|                 processStderr(); | ||||
|                 conn->processStderr(); | ||||
|             } catch (EndOfFile & e) { } | ||||
|         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"); | ||||
| 
 | ||||
|     openConnection(); | ||||
|     to << wopAddTextToStore << name << s << references; | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopAddTextToStore << name << s << references; | ||||
| 
 | ||||
|     processStderr(); | ||||
|     return readStorePath(from); | ||||
|     conn->processStderr(); | ||||
|     return readStorePath(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::exportPath(const Path & path, bool sign, | ||||
|     Sink & sink) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopExportPath << path << (sign ? 1 : 0); | ||||
|     processStderr(&sink); /* sink receives the actual data */ | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopExportPath << path << (sign ? 1 : 0); | ||||
|     conn->processStderr(&sink); /* sink receives the actual data */ | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Paths RemoteStore::importPaths(bool requireSignature, Source & source) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopImportPaths; | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopImportPaths; | ||||
|     /* We ignore requireSignature, since the worker forces it to true
 | ||||
|        anyway. */ | ||||
|     processStderr(0, &source); | ||||
|     return readStorePaths<Paths>(from); | ||||
|     conn->processStderr(0, &source); | ||||
|     return readStorePaths<Paths>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopBuildPaths; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 13) { | ||||
|         to << drvPaths; | ||||
|         if (GET_PROTOCOL_MINOR(daemonVersion) >= 15) | ||||
|             to << buildMode; | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopBuildPaths; | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13) { | ||||
|         conn->to << drvPaths; | ||||
|         if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 15) | ||||
|             conn->to << buildMode; | ||||
|         else | ||||
|             /* Old daemons did not take a 'buildMode' parameter, so we
 | ||||
|                need to validate it here on the client side.  */ | ||||
|  | @ -449,22 +444,22 @@ void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode) | |||
|         PathSet drvPaths2; | ||||
|         for (auto & i : drvPaths) | ||||
|             drvPaths2.insert(string(i, 0, i.find('!'))); | ||||
|         to << drvPaths2; | ||||
|         conn->to << drvPaths2; | ||||
|     } | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDerivation & drv, | ||||
|     BuildMode buildMode) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopBuildDerivation << drvPath << drv << buildMode; | ||||
|     processStderr(); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopBuildDerivation << drvPath << drv << buildMode; | ||||
|     conn->processStderr(); | ||||
|     BuildResult res; | ||||
|     unsigned int status; | ||||
|     from >> status >> res.errorMsg; | ||||
|     conn->from >> status >> res.errorMsg; | ||||
|     res.status = (BuildResult::Status) status; | ||||
|     return res; | ||||
| } | ||||
|  | @ -472,50 +467,50 @@ BuildResult RemoteStore::buildDerivation(const Path & drvPath, const BasicDeriva | |||
| 
 | ||||
| void RemoteStore::ensurePath(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopEnsurePath << path; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopEnsurePath << path; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::addTempRoot(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopAddTempRoot << path; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopAddTempRoot << path; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::addIndirectRoot(const Path & path) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopAddIndirectRoot << path; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopAddIndirectRoot << path; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::syncWithGC() | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopSyncWithGC; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopSyncWithGC; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| Roots RemoteStore::findRoots() | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopFindRoots; | ||||
|     processStderr(); | ||||
|     unsigned int count = readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopFindRoots; | ||||
|     conn->processStderr(); | ||||
|     unsigned int count = readInt(conn->from); | ||||
|     Roots result; | ||||
|     while (count--) { | ||||
|         Path link = readString(from); | ||||
|         Path target = readStorePath(from); | ||||
|         Path link = readString(conn->from); | ||||
|         Path target = readStorePath(conn->from); | ||||
|         result[link] = target; | ||||
|     } | ||||
|     return result; | ||||
|  | @ -524,56 +519,56 @@ Roots RemoteStore::findRoots() | |||
| 
 | ||||
| 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; | ||||
|     if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) | ||||
|     if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 5) | ||||
|         /* removed options */ | ||||
|         to << 0 << 0; | ||||
|         conn->to << 0 << 0; | ||||
| 
 | ||||
|     processStderr(); | ||||
|     conn->processStderr(); | ||||
| 
 | ||||
|     results.paths = readStrings<PathSet>(from); | ||||
|     results.bytesFreed = readLongLong(from); | ||||
|     readLongLong(from); // obsolete
 | ||||
|     results.paths = readStrings<PathSet>(conn->from); | ||||
|     results.bytesFreed = readLongLong(conn->from); | ||||
|     readLongLong(conn->from); // obsolete
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| PathSet RemoteStore::queryFailedPaths() | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopQueryFailedPaths; | ||||
|     processStderr(); | ||||
|     return readStorePaths<PathSet>(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopQueryFailedPaths; | ||||
|     conn->processStderr(); | ||||
|     return readStorePaths<PathSet>(conn->from); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void RemoteStore::clearFailedPaths(const PathSet & paths) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopClearFailedPaths << paths; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopClearFailedPaths << paths; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| void RemoteStore::optimiseStore() | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopOptimiseStore; | ||||
|     processStderr(); | ||||
|     readInt(from); | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopOptimiseStore; | ||||
|     conn->processStderr(); | ||||
|     readInt(conn->from); | ||||
| } | ||||
| 
 | ||||
| bool RemoteStore::verifyStore(bool checkContents, bool repair) | ||||
| { | ||||
|     openConnection(); | ||||
|     to << wopVerifyStore << checkContents << repair; | ||||
|     processStderr(); | ||||
|     return readInt(from) != 0; | ||||
|     auto conn(connections->get()); | ||||
|     conn->to << wopVerifyStore << checkContents << repair; | ||||
|     conn->processStderr(); | ||||
|     return readInt(conn->from) != 0; | ||||
| } | ||||
| 
 | ||||
| void RemoteStore::processStderr(Sink * sink, Source * source) | ||||
| void RemoteStore::Connection::processStderr(Sink * sink, Source * source) | ||||
| { | ||||
|     to.flush(); | ||||
|     unsigned int msg; | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ class Pipe; | |||
| class Pid; | ||||
| struct FdSink; | ||||
| struct FdSource; | ||||
| template<typename T> class Pool; | ||||
| 
 | ||||
| 
 | ||||
| class RemoteStore : public Store | ||||
|  | @ -91,19 +92,22 @@ public: | |||
|     bool verifyStore(bool checkContents, bool repair) override; | ||||
| 
 | ||||
| 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