- Revision
- 143271
- Author
- [email protected]
- Date
- 2013-02-18 15:42:24 -0800 (Mon, 18 Feb 2013)
Log Message
Fix SQLTransaction leak.
https://bugs.webkit.org/show_bug.cgi?id=110052.
Reviewed by Geoffrey Garen.
With https://bugs.webkit.org/show_bug.cgi?id=104750, there is now a circular
reference between SQLTransaction and its backend. The clean up process needs
to be fixed to explicitly break this reference cycle.
The 5 phases of the SQLTransaction (and backend) phases and their clean up
actions are:
Phase 1. After Birth, before scheduling
- During shutdown, DatabaseThread::databaseThread() calls
DatabaseBackendAsync::close(). DatabaseBackendAsync::close() iterates
DatabaseBackendAsync::m_transactionQueue and calls
SQLtransactionBackend::notifyDatabaseThreadIsShuttingDown() on each
transaction there.
Phase 2. After scheduling, before state AcquireLock
- ~DatabaseTask() calls SQLtransactionBackend's
notifyDatabaseThreadIsShuttingDown().
Phase 3. After state AcquireLock, before "lockAcquired"
- During shutdown, DatabaseThread::databaseThread() calls
SQLTransactionCoordinator::shutdown(), which calls
SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown().
Phase 4: After "lockAcquired", before state CleanupAndTerminate
- Same as Phase 3.
Phase 5: After state CleanupAndTerminate
- state CleanupAndTerminate calls SQLTransactionBackend::doCleanup().
See comment at the top of SQLTransactionBackend.cpp for more details.
Other supporting changes:
- Moved Database::close() to the DatabaseBackendAsync.
- Moved the "if already cleaned up" check from SQLTransactionBackend's
notifyDatabaseThreadIsShuttingDown() to doCleanup().
- Added a check to prevent SQLTransactionCoordinator's releaseLock()
from running when it's shutting down.
No new tests.
* Modules/webdatabase/Database.cpp:
* Modules/webdatabase/Database.h:
* Modules/webdatabase/DatabaseBackendAsync.cpp:
(WebCore::DatabaseBackendAsync::close): Move from Database.cpp.
* Modules/webdatabase/DatabaseBackendAsync.h:
* Modules/webdatabase/DatabaseTask.cpp:
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask):
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask):
(WebCore::DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask):
* Modules/webdatabase/DatabaseTask.h:
(DatabaseBackendAsync::DatabaseTransactionTask):
* Modules/webdatabase/DatabaseThread.cpp:
(WebCore::DatabaseThread::databaseThread):
* Modules/webdatabase/SQLTransactionBackend.cpp:
(WebCore::SQLTransactionBackend::doCleanup):
(WebCore::SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown):
(WebCore::SQLTransactionBackend::cleanupAndTerminate):
* Modules/webdatabase/SQLTransactionCoordinator.cpp:
(WebCore::SQLTransactionCoordinator::SQLTransactionCoordinator):
(WebCore::SQLTransactionCoordinator::acquireLock):
(WebCore::SQLTransactionCoordinator::releaseLock):
(WebCore::SQLTransactionCoordinator::shutdown):
* Modules/webdatabase/SQLTransactionCoordinator.h:
(SQLTransactionCoordinator):
Modified Paths
Diff
Modified: trunk/Source/WebCore/ChangeLog (143270 => 143271)
--- trunk/Source/WebCore/ChangeLog 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/ChangeLog 2013-02-18 23:42:24 UTC (rev 143271)
@@ -1,3 +1,75 @@
+2013-02-17 Mark Lam <[email protected]>
+
+ Fix SQLTransaction leak.
+ https://bugs.webkit.org/show_bug.cgi?id=110052.
+
+ Reviewed by Geoffrey Garen.
+
+ With https://bugs.webkit.org/show_bug.cgi?id=104750, there is now a circular
+ reference between SQLTransaction and its backend. The clean up process needs
+ to be fixed to explicitly break this reference cycle.
+
+ The 5 phases of the SQLTransaction (and backend) phases and their clean up
+ actions are:
+
+ Phase 1. After Birth, before scheduling
+ - During shutdown, DatabaseThread::databaseThread() calls
+ DatabaseBackendAsync::close(). DatabaseBackendAsync::close() iterates
+ DatabaseBackendAsync::m_transactionQueue and calls
+ SQLtransactionBackend::notifyDatabaseThreadIsShuttingDown() on each
+ transaction there.
+
+ Phase 2. After scheduling, before state AcquireLock
+ - ~DatabaseTask() calls SQLtransactionBackend's
+ notifyDatabaseThreadIsShuttingDown().
+
+ Phase 3. After state AcquireLock, before "lockAcquired"
+ - During shutdown, DatabaseThread::databaseThread() calls
+ SQLTransactionCoordinator::shutdown(), which calls
+ SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown().
+
+ Phase 4: After "lockAcquired", before state CleanupAndTerminate
+ - Same as Phase 3.
+
+ Phase 5: After state CleanupAndTerminate
+ - state CleanupAndTerminate calls SQLTransactionBackend::doCleanup().
+
+ See comment at the top of SQLTransactionBackend.cpp for more details.
+
+ Other supporting changes:
+ - Moved Database::close() to the DatabaseBackendAsync.
+ - Moved the "if already cleaned up" check from SQLTransactionBackend's
+ notifyDatabaseThreadIsShuttingDown() to doCleanup().
+ - Added a check to prevent SQLTransactionCoordinator's releaseLock()
+ from running when it's shutting down.
+
+ No new tests.
+
+ * Modules/webdatabase/Database.cpp:
+ * Modules/webdatabase/Database.h:
+ * Modules/webdatabase/DatabaseBackendAsync.cpp:
+ (WebCore::DatabaseBackendAsync::close): Move from Database.cpp.
+ * Modules/webdatabase/DatabaseBackendAsync.h:
+ * Modules/webdatabase/DatabaseTask.cpp:
+ (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask):
+ (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask):
+ (WebCore::DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask):
+ * Modules/webdatabase/DatabaseTask.h:
+ (DatabaseBackendAsync::DatabaseTransactionTask):
+ * Modules/webdatabase/DatabaseThread.cpp:
+ (WebCore::DatabaseThread::databaseThread):
+ * Modules/webdatabase/SQLTransactionBackend.cpp:
+ (WebCore::SQLTransactionBackend::doCleanup):
+ (WebCore::SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown):
+ (WebCore::SQLTransactionBackend::cleanupAndTerminate):
+ * Modules/webdatabase/SQLTransactionCoordinator.cpp:
+ (WebCore::SQLTransactionCoordinator::SQLTransactionCoordinator):
+ (WebCore::SQLTransactionCoordinator::acquireLock):
+ (WebCore::SQLTransactionCoordinator::releaseLock):
+ (WebCore::SQLTransactionCoordinator::shutdown):
+ * Modules/webdatabase/SQLTransactionCoordinator.h:
+ (SQLTransactionCoordinator):
+
2013-02-18 David Kilzer <[email protected]>
Fix WebCore Xcode project
Modified: trunk/Source/WebCore/Modules/webdatabase/Database.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/Database.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -159,19 +159,6 @@
synchronizer.waitForTaskCompletion();
}
-void Database::close()
-{
- ASSERT(databaseContext()->databaseThread());
- ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
-
- closeDatabase();
-
- // Must ref() before calling databaseThread()->recordDatabaseClosed().
- RefPtr<Database> protect = this;
- databaseContext()->databaseThread()->recordDatabaseClosed(this);
- databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
-}
-
void Database::closeImmediately()
{
ASSERT(m_scriptExecutionContext->isContextThread());
Modified: trunk/Source/WebCore/Modules/webdatabase/Database.h (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/Database.h 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/Database.h 2013-02-18 23:42:24 UTC (rev 143271)
@@ -71,7 +71,6 @@
virtual void markAsDeletedAndClose();
bool deleted() const { return m_deleted; }
- void close();
virtual void closeImmediately();
unsigned long long databaseSize() const;
Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -78,6 +78,40 @@
return false;
}
+void DatabaseBackendAsync::close()
+{
+ ASSERT(databaseContext()->databaseThread());
+ ASSERT(currentThread() == databaseContext()->databaseThread()->getThreadID());
+
+ {
+ MutexLocker locker(m_transactionInProgressMutex);
+
+ // Clean up transactions that have not been scheduled yet:
+ // Transaction phase 1 cleanup. See comment on "What happens if a
+ // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+ RefPtr<SQLTransactionBackend> transaction;
+ while (!m_transactionQueue.isEmpty()) {
+ transaction = m_transactionQueue.takeFirst();
+ transaction->notifyDatabaseThreadIsShuttingDown();
+ }
+
+ m_isTransactionQueueEnabled = false;
+ m_transactionInProgress = false;
+ }
+
+ closeDatabase();
+
+ // DatabaseThread keeps databases alive by referencing them in its
+ // m_openDatabaseSet. DatabaseThread::recordDatabaseClose() will remove
+ // this database from that set (which effectively deref's it). We hold on
+ // to it with a local pointer here for a liitle longer, so that we can
+ // unschedule any DatabaseTasks that refer to it before the database gets
+ // deleted.
+ RefPtr<DatabaseBackendAsync> protect = this;
+ databaseContext()->databaseThread()->recordDatabaseClosed(this);
+ databaseContext()->databaseThread()->unscheduleDatabaseTasks(this);
+}
+
PassRefPtr<SQLTransactionBackend> DatabaseBackendAsync::runTransaction(PassRefPtr<SQLTransaction> transaction,
bool readOnly, const ChangeVersionData* data)
{
Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseBackendAsync.h 2013-02-18 23:42:24 UTC (rev 143271)
@@ -53,6 +53,7 @@
DatabaseBackendAsync(PassRefPtr<DatabaseBackendContext>, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize);
virtual bool openAndVerifyVersion(bool setVersionInNewDatabase, DatabaseError&, String& errorMessage);
+ void close();
PassRefPtr<SQLTransactionBackend> runTransaction(PassRefPtr<SQLTransaction>, bool readOnly, const ChangeVersionData*);
void scheduleTransactionStep(SQLTransactionBackend*);
Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -150,12 +150,28 @@
DatabaseBackendAsync::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransactionBackend> transaction)
: DatabaseTask(Database::from(transaction->database()), 0)
, m_transaction(transaction)
+ , m_didPerformTask(false)
{
}
+DatabaseBackendAsync::DatabaseTransactionTask::~DatabaseTransactionTask()
+{
+ // If the task is being destructed without the transaction ever being run,
+ // then we must either have an error or an interruption. Give the
+ // transaction a chance to clean up since it may not have been able to
+ // run to its clean up state.
+
+ // Transaction phase 2 cleanup. See comment on "What happens if a
+ // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+
+ if (!m_didPerformTask)
+ m_transaction->notifyDatabaseThreadIsShuttingDown();
+}
+
void DatabaseBackendAsync::DatabaseTransactionTask::doPerformTask()
{
m_transaction->performNextStep();
+ m_didPerformTask = true;
}
#if !LOG_DISABLED
Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseTask.h 2013-02-18 23:42:24 UTC (rev 143271)
@@ -137,6 +137,8 @@
class DatabaseBackendAsync::DatabaseTransactionTask : public DatabaseTask {
public:
+ virtual ~DatabaseTransactionTask();
+
// Transaction task is never synchronous, so no 'synchronizer' parameter.
static PassOwnPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransactionBackend> transaction)
{
@@ -154,6 +156,7 @@
#endif
RefPtr<SQLTransactionBackend> m_transaction;
+ bool m_didPerformTask;
};
class DatabaseBackendAsync::DatabaseTableNamesTask : public DatabaseTask {
Modified: trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/DatabaseThread.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -126,7 +126,7 @@
openSetCopy.swap(m_openDatabaseSet);
DatabaseSet::iterator end = openSetCopy.end();
for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it)
- Database::from((*it).get())->close();
+ (*it).get()->close();
}
// Detach the thread so its resources are no longer of any concern to anyone else
Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -285,15 +285,58 @@
// What happens if a transaction is interrupted?
// ============================================
// If the transaction is interrupted half way, it won't get to run to state
-// CleanupAndTerminate, and hence, would not have called doCleanup(). doCleanup() is
-// where we nullify SQLTransactionBackend::m_frontend to break the reference cycle
-// between the frontend and backend. Hence, that transaction will never get cleaned
-// up, unless ...
+// CleanupAndTerminate, and hence, would not have called SQLTransactionBackend's
+// doCleanup(). doCleanup() is where we nullify SQLTransactionBackend::m_frontend
+// to break the reference cycle between the frontend and backend. Hence, we need
+// to cleanup the transaction by other means.
//
-// ... unless we have a cleanup mechanism for interruptions. That is, during the
-// shutdown of the DatabaseThread, the SQLTransactionCoordinator will discover this
-// interrupted transaction and invoke its notifyDatabaseThreadIsShuttingDown(),
-// which in turn will call doCleanup(). With that, all is well again.
+// Note: calling SQLTransactionBackend::notifyDatabaseThreadIsShuttingDown()
+// is effectively the same as calling SQLTransactionBackend::doClean().
+//
+// In terms of who needs to call doCleanup(), there are 5 phases in the
+// SQLTransactionBackend life-cycle. These are the phases and how the clean
+// up is done:
+//
+// Phase 1. After Birth, before scheduling
+//
+// - To clean up, DatabaseThread::databaseThread() will call
+// DatabaseBackendAsync::close() during its shutdown.
+// - DatabaseBackendAsync::close() will iterate
+// DatabaseBackendAsync::m_transactionQueue and call
+// notifyDatabaseThreadIsShuttingDown() on each transaction there.
+//
+// Phase 2. After scheduling, before state AcquireLock
+//
+// - If the interruption occures before the DatabaseTransactionTask is
+// scheduled in DatabaseThread::m_queue but hasn't gotten to execute
+// (i.e. DatabaseTransactionTask::performTask() has not been called),
+// then the DatabaseTransactionTask may get destructed before it ever
+// gets to execute.
+// - To clean up, the destructor will check if the task's m_wasExecuted is
+// set. If not, it will call notifyDatabaseThreadIsShuttingDown() on
+// the task's transaction.
+//
+// Phase 3. After state AcquireLock, before "lockAcquired"
+//
+// - In this phase, the transaction would have been added to the
+// SQLTransactionCoordinator's CoordinationInfo's pendingTransactions.
+// - To clean up, during shutdown, DatabaseThread::databaseThread() calls
+// SQLTransactionCoordinator::shutdown(), which calls
+// notifyDatabaseThreadIsShuttingDown().
+//
+// Phase 4: After "lockAcquired", before state CleanupAndTerminate
+//
+// - In this phase, the transaction would have been added either to the
+// SQLTransactionCoordinator's CoordinationInfo's activeWriteTransaction
+// or activeReadTransactions.
+// - To clean up, during shutdown, DatabaseThread::databaseThread() calls
+// SQLTransactionCoordinator::shutdown(), which calls
+// notifyDatabaseThreadIsShuttingDown().
+//
+// Phase 5: After state CleanupAndTerminate
+//
+// - This is how a transaction ends normally.
+// - state CleanupAndTerminate calls doCleanup().
// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit.
@@ -335,6 +378,9 @@
void SQLTransactionBackend::doCleanup()
{
+ if (!m_frontend)
+ return;
+
ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID());
MutexLocker locker(m_statementMutex);
@@ -481,11 +527,7 @@
// DB thread. Amongst other work, doCleanup() will clear m_sqliteTransaction
// which invokes SQLiteTransaction's destructor, which will do the roll back
// if necessary.
-
- // Note: if doCleanup() has already been invoked, then m_frontend would have
- // been nullified.
- if (m_frontend)
- doCleanup();
+ doCleanup();
}
SQLTransactionState SQLTransactionBackend::acquireLock()
@@ -719,6 +761,7 @@
LOG(StorageAPI, "Transaction %p is complete\n", this);
ASSERT(!m_database->sqliteDatabase().transactionInProgress());
+ // Phase 5 cleanup. See comment on the SQLTransaction life-cycle above.
doCleanup();
m_database->inProgressTransactionCompleted();
return SQLTransactionState::End;
Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.cpp 2013-02-18 23:42:24 UTC (rev 143271)
@@ -50,6 +50,11 @@
return database->stringIdentifier();
}
+SQLTransactionCoordinator::SQLTransactionCoordinator()
+ : m_isShuttingDown(false)
+{
+}
+
void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info)
{
if (info.activeWriteTransaction || info.pendingTransactions.isEmpty())
@@ -71,6 +76,8 @@
void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction)
{
+ ASSERT(!m_isShuttingDown);
+
String dbIdentifier = getDatabaseIdentifier(transaction);
CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier);
@@ -86,7 +93,7 @@
void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction)
{
- if (m_coordinationInfoMap.isEmpty())
+ if (m_isShuttingDown)
return;
String dbIdentifier = getDatabaseIdentifier(transaction);
@@ -108,10 +115,18 @@
void SQLTransactionCoordinator::shutdown()
{
+ // Prevent releaseLock() from accessing / changing the coordinationInfo
+ // while we're shutting down.
+ m_isShuttingDown = true;
+
// Notify all transactions in progress that the database thread is shutting down
for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin();
coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) {
CoordinationInfo& info = coordinationInfoIterator->value;
+
+ // Clean up transactions that have reached "lockAcquired":
+ // Transaction phase 4 cleanup. See comment on "What happens if a
+ // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
if (info.activeWriteTransaction)
info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown();
for (HashSet<RefPtr<SQLTransactionBackend> >::iterator activeReadTransactionsIterator =
@@ -120,6 +135,14 @@
++activeReadTransactionsIterator) {
(*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown();
}
+
+ // Clean up transactions that have NOT reached "lockAcquired":
+ // Transaction phase 3 cleanup. See comment on "What happens if a
+ // transaction is interrupted?" at the top of SQLTransactionBackend.cpp.
+ while (!info.pendingTransactions.isEmpty()) {
+ RefPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first();
+ transaction->notifyDatabaseThreadIsShuttingDown();
+ }
}
// Clean up all pending transactions for all databases
Modified: trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h (143270 => 143271)
--- trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h 2013-02-18 23:29:56 UTC (rev 143270)
+++ trunk/Source/WebCore/Modules/webdatabase/SQLTransactionCoordinator.h 2013-02-18 23:42:24 UTC (rev 143271)
@@ -47,7 +47,7 @@
class SQLTransactionCoordinator {
WTF_MAKE_NONCOPYABLE(SQLTransactionCoordinator); WTF_MAKE_FAST_ALLOCATED;
public:
- SQLTransactionCoordinator() { }
+ SQLTransactionCoordinator();
void acquireLock(SQLTransactionBackend*);
void releaseLock(SQLTransactionBackend*);
void shutdown();
@@ -61,6 +61,7 @@
// Maps database names to information about pending transactions
typedef HashMap<String, CoordinationInfo> CoordinationInfoMap;
CoordinationInfoMap m_coordinationInfoMap;
+ bool m_isShuttingDown;
void processPendingTransactions(CoordinationInfo&);
};