@@ -71,29 +71,38 @@ static int filesForProvider_compare(const MDB_val *a, const MDB_val *b) {
71
71
return IDCode::compare (lhs->UnitCode , rhs->UnitCode );
72
72
}
73
73
74
+ // / Returns a global serial queue for stale database removal.
75
+ static dispatch_queue_t getDiscardedDBsCleanupQueue () {
76
+ static dispatch_queue_t queue;
77
+ static dispatch_once_t onceToken = 0 ;
78
+ dispatch_once (&onceToken, ^{
79
+ dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class (DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0 );
80
+ queue = dispatch_queue_create (" indexstoredb.db.discarded_dbs_cleanup" , qosAttribute);
81
+ });
82
+ return queue;
83
+ }
84
+
74
85
Database::Implementation::Implementation () {
75
86
ReadTxnGroup = dispatch_group_create ();
76
87
TxnSyncQueue = dispatch_queue_create (" indexstoredb.db.txn_sync" , DISPATCH_QUEUE_CONCURRENT);
77
- dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class (DISPATCH_QUEUE_SERIAL, QOS_CLASS_BACKGROUND, 0 );
78
- DiscardedDBsCleanupQueue = dispatch_queue_create (" indexstoredb.db.discarded_dbs_cleanup" , qosAttribute);
79
88
}
80
89
Database::Implementation::~Implementation () {
81
90
if (!IsReadOnly) {
82
91
DBEnv.close ();
92
+ assert (!SavedPath.empty () && !UniquePath.empty ());
83
93
// In case some other process already created the 'saved' path, override it so
84
94
// that the 'last one wins'.
85
- llvm::sys::fs::rename (SavedPath, llvm::Twine (ProcessPath )+" saved" +DeadProcessDBSuffix);
86
- if (std::error_code ec = llvm::sys::fs::rename (ProcessPath , SavedPath)) {
95
+ llvm::sys::fs::rename (SavedPath, llvm::Twine (UniquePath )+" - saved" +DeadProcessDBSuffix);
96
+ if (std::error_code ec = llvm::sys::fs::rename (UniquePath , SavedPath)) {
87
97
// If the database directory already got removed or some other process beat
88
98
// us during the tiny window between the above 2 renames, then give-up,
89
99
// and let the database to be discarded.
90
- LOG_WARN_FUNC (" failed moving process directory to 'saved': " << ec.message ());
100
+ LOG_WARN_FUNC (" failed moving " << llvm::sys::path::filename (UniquePath) << " directory to 'saved': " << ec.message ());
91
101
}
92
102
}
93
103
94
104
dispatch_release (ReadTxnGroup);
95
105
dispatch_release (TxnSyncQueue);
96
- dispatch_release (DiscardedDBsCleanupQueue);
97
106
}
98
107
99
108
std::shared_ptr<Database::Implementation>
@@ -105,12 +114,14 @@ Database::Implementation::create(StringRef path, bool readonly, Optional<size_t>
105
114
106
115
SmallString<128 > savedPathBuf = versionPath;
107
116
llvm::sys::path::append (savedPathBuf, " saved" );
108
- SmallString<128 > processPathBuf = versionPath;
117
+ SmallString<128 > prefixPathBuf = versionPath;
109
118
#if defined(WIN32)
110
- llvm::raw_svector_ostream (processPathBuf ) << " /p" << GetCurrentProcess ();
119
+ llvm::raw_svector_ostream (prefixPathBuf ) << " /p" << GetCurrentProcess ();
111
120
#else
112
- llvm::raw_svector_ostream (processPathBuf ) << " /p" << getpid ();
121
+ llvm::raw_svector_ostream (prefixPathBuf ) << " /p" << getpid ();
113
122
#endif
123
+ llvm::raw_svector_ostream (prefixPathBuf) << " -" ;
124
+ SmallString<128 > uniqueDirPath;
114
125
115
126
bool existingDB = true ;
116
127
@@ -121,30 +132,33 @@ Database::Implementation::create(StringRef path, bool readonly, Optional<size_t>
121
132
}
122
133
return false ;
123
134
};
135
+ auto createUniqueDirOrError = [&error, &prefixPathBuf, &uniqueDirPath]() {
136
+ uniqueDirPath.clear ();
137
+ if (std::error_code ec = llvm::sys::fs::createUniqueDirectory (prefixPathBuf, uniqueDirPath)) {
138
+ llvm::raw_string_ostream (error) << " failed creating directory '" << uniqueDirPath << " ': " << ec.message ();
139
+ return true ;
140
+ }
141
+ return false ;
142
+ };
124
143
125
144
StringRef dbPath;
126
145
if (!readonly) {
127
146
if (createDirectoriesOrError (versionPath))
128
147
return nullptr ;
129
148
130
- // Move the currently stored database to a process-specific directory.
131
- // When the database closes it moves the process-specific directory back to
149
+ // Move the currently stored database to a unique directory to isolate it .
150
+ // When the database closes it moves the unique directory back to
132
151
// the '/saved' one. If we crash before closing, then we'll discard the database
133
- // that is left in the process-specific one.
134
-
135
- // In case some other process that had the same pid crashed and left the database
136
- // directory, move it aside and we'll clear it later.
137
- // We are already protected from opening the same database twice from the same
138
- // process via getLMDBDatabaseRefForPath().
139
- llvm::sys::fs::rename (processPathBuf, llvm::Twine (processPathBuf)+DeadProcessDBSuffix);
152
+ // that is left in the unique directory that includes the process pid number.
153
+ if (createUniqueDirOrError ())
154
+ return nullptr ;
140
155
141
- if (llvm::sys::fs::rename (savedPathBuf, processPathBuf)) {
142
- // No existing database, create a new directory.
156
+ // This succeeds for moving to an empty directory, like the newly constructed `uniqueDirPath`.
157
+ if (llvm::sys::fs::rename (savedPathBuf, uniqueDirPath)) {
158
+ // No existing database, just use the new directory.
143
159
existingDB = false ;
144
- if (createDirectoriesOrError (processPathBuf))
145
- return nullptr ;
146
160
}
147
- dbPath = processPathBuf ;
161
+ dbPath = uniqueDirPath ;
148
162
} else {
149
163
dbPath = savedPathBuf;
150
164
}
@@ -155,7 +169,7 @@ Database::Implementation::create(StringRef path, bool readonly, Optional<size_t>
155
169
db->IsReadOnly = readonly;
156
170
db->VersionedPath = versionPath.str ();
157
171
db->SavedPath = savedPathBuf.str ();
158
- db->ProcessPath = processPathBuf .str ();
172
+ db->UniquePath = uniqueDirPath .str ();
159
173
db->DBEnv = lmdb::env::create ();
160
174
db->DBEnv .set_max_dbs (14 );
161
175
@@ -215,8 +229,8 @@ Database::Implementation::create(StringRef path, bool readonly, Optional<size_t>
215
229
" corrupted database saved at '" << corruptedPathBuf << " '\n "
216
230
" creating new database..." );
217
231
218
- // Recreate the process path for the next attempt.
219
- if (createDirectoriesOrError (processPathBuf ))
232
+ // Recreate the unique database path for the next attempt.
233
+ if (!readonly && createUniqueDirOrError ( ))
220
234
return nullptr ;
221
235
existingDB = false ;
222
236
goto retry;
@@ -300,7 +314,7 @@ static void cleanupDiscardedDBsImpl(StringRef versionedPath) {
300
314
301
315
// Finds database subdirectories that are considered dead and removes them.
302
316
// A directory is dead if it has been marked with the suffix "-dead" or if it
303
- // has the name "p<PID>" where process PID is no longer running.
317
+ // has the name "p<PID>-* " where process PID is no longer running.
304
318
305
319
#if defined(WIN32)
306
320
indexstorePid_t currPID = GetCurrentProcess ();
@@ -321,16 +335,22 @@ static void cleanupDiscardedDBsImpl(StringRef versionedPath) {
321
335
return true ;
322
336
if (!path.startswith (" p" ))
323
337
return false ;
338
+ StringRef pidStr = path.substr (1 );
339
+ size_t dashIdx = pidStr.find (' -' );
340
+ if (dashIdx == StringRef::npos)
341
+ return false ;
342
+ pidStr = pidStr.substr (0 , dashIdx);
324
343
size_t pathPID;
325
- if (path. substr ( 1 ) .getAsInteger (10 , pathPID))
344
+ if (pidStr .getAsInteger (10 , pathPID))
326
345
return false ;
327
346
if ((indexstorePid_t)pathPID == currPID)
328
347
return false ;
329
348
return !isProcessStillExecuting ((indexstorePid_t)pathPID);
330
349
};
331
350
332
351
if (shouldRemove (currPath, currPID)) {
333
- remove_directories (currPath);
352
+ // FIXME: With `IgnoreErrors` set to true it can hit an assertion if an error occurs.
353
+ remove_directories (currPath, /* IgnoreErrors=*/ false );
334
354
}
335
355
336
356
Begin.increment (EC);
@@ -339,7 +359,7 @@ static void cleanupDiscardedDBsImpl(StringRef versionedPath) {
339
359
340
360
void Database::Implementation::cleanupDiscardedDBs () {
341
361
std::string localVersionedPath = VersionedPath;
342
- dispatch_async (DiscardedDBsCleanupQueue , ^{
362
+ dispatch_async (getDiscardedDBsCleanupQueue () , ^{
343
363
cleanupDiscardedDBsImpl (localVersionedPath);
344
364
});
345
365
}
0 commit comments