Below is the list of changes that have just been committed into a local
5.1 repository of jonas. When jonas does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-11-12 17:13:49+01:00, jonas@stripped +15 -0
ndb - DictLock
Move LockQueue impl. from DbUtil into own class
Create LockQueue instance in Dbdict to handle DictLock (remove old poll-based impl.)
Remove cs_blockState and use DictLock instead
Add Shared lock(s) to LockQueue and Mutex
Note: It would be much nicer if this was a "clean" impl (i.e using DbUtil)
but it's a lot of work to add a timeslice to each schema operation
storage/ndb/include/kernel/signaldata/DictLock.hpp@stripped, 2007-11-12 17:13:45+01:00,
jonas@stripped +15 -3
Add lock-types for each schema operation (for traceability)
storage/ndb/include/kernel/signaldata/UtilLock.hpp@stripped, 2007-11-12 17:13:45+01:00,
jonas@stripped +11 -7
Add more options to UtilLock
storage/ndb/src/common/debugger/signaldata/UtilLock.cpp@stripped, 2007-11-12 17:13:45+01:00,
jonas@stripped +1 -3
Add more options to UtilLock
storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp@stripped, 2007-11-12 17:13:45+01:00,
jonas@stripped +430 -375
remove c_blockState with LockQueue (which is same as UtilLock)
storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +32 -108
remove c_blockState with LockQueue (which is same as UtilLock)
storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +2 -0
Adopt to protovol change
storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +154 -103
More Lock impl. into separate class so that it can be used in Dict aswell
storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +13 -24
More Lock impl. into separate class so that it can be used in Dict aswell
storage/ndb/src/kernel/vm/LockQueue.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +206 -0
New BitKeeper file ``storage/ndb/src/kernel/vm/LockQueue.cpp''
storage/ndb/src/kernel/vm/LockQueue.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +0 -0
storage/ndb/src/kernel/vm/LockQueue.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +79 -0
New BitKeeper file ``storage/ndb/src/kernel/vm/LockQueue.hpp''
storage/ndb/src/kernel/vm/LockQueue.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +0 -0
storage/ndb/src/kernel/vm/Makefile.am@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +1 -1
Add LockQueue (stolen from DbUtil)
storage/ndb/src/kernel/vm/Mutex.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +18 -26
Add new mutex options
- Notify
- SharedLock
storage/ndb/src/kernel/vm/Mutex.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +12 -7
Add new mutex options
- Notify
- SharedLock
storage/ndb/src/kernel/vm/SimulatedBlock.cpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +2 -1
Add new mutex options
- Notify
- SharedLock
storage/ndb/src/kernel/vm/SimulatedBlock.hpp@stripped, 2007-11-12 17:13:46+01:00,
jonas@stripped +2 -3
Add new mutex options
- Notify
- SharedLock
diff -Nrup a/storage/ndb/include/kernel/signaldata/DictLock.hpp
b/storage/ndb/include/kernel/signaldata/DictLock.hpp
--- a/storage/ndb/include/kernel/signaldata/DictLock.hpp 2006-12-23 20:20:04 +01:00
+++ b/storage/ndb/include/kernel/signaldata/DictLock.hpp 2007-11-12 17:13:45 +01:00
@@ -26,8 +26,18 @@ class DictLockReq {
public:
STATIC_CONST( SignalLength = 3 );
enum LockType {
- NoLock = 0,
- NodeRestartLock = 1
+ NoLock = 0
+ ,NodeRestartLock = 1 // S-lock
+ ,NodeFailureLock = 2 // S-lock
+ ,CreateTableLock = 3
+ ,AlterTableLock = 4
+ ,DropTableLock = 5
+ ,CreateIndexLock = 6
+ ,DropIndexLock = 7
+ ,CreateFileLock = 8
+ ,CreateFilegroupLock = 9
+ ,DropFileLock = 10
+ ,DropFilegroupLock = 11
};
private:
Uint32 userPtr;
@@ -68,10 +78,12 @@ class DictUnlockOrd {
friend class Dbdict;
friend class Dbdih;
public:
- STATIC_CONST( SignalLength = 2 );
+ STATIC_CONST( SignalLength = 4 );
private:
Uint32 lockPtr;
Uint32 lockType;
+ Uint32 senderData;
+ Uint32 senderRef;
};
#endif
diff -Nrup a/storage/ndb/include/kernel/signaldata/UtilLock.hpp
b/storage/ndb/include/kernel/signaldata/UtilLock.hpp
--- a/storage/ndb/include/kernel/signaldata/UtilLock.hpp 2006-12-23 20:20:07 +01:00
+++ b/storage/ndb/include/kernel/signaldata/UtilLock.hpp 2007-11-12 17:13:45 +01:00
@@ -36,13 +36,18 @@ public:
STATIC_CONST( SignalLength = 4 );
enum RequestInfo {
- TryLock = 1
+ TryLock = 1,
+ SharedLock = 2,
+ Notify = 4,
+ Granted = 8
};
+
public:
Uint32 senderData;
Uint32 senderRef;
Uint32 lockId;
Uint32 requestInfo;
+ Uint32 extra;
};
class UtilLockConf {
@@ -66,7 +71,6 @@ public:
Uint32 senderData;
Uint32 senderRef;
Uint32 lockId;
- Uint32 lockKey;
};
class UtilLockRef {
@@ -91,10 +95,11 @@ public:
NoSuchLock = 1,
OutOfLockRecords = 2,
DistributedLockNotSupported = 3,
- LockAlreadyHeld = 4
-
+ LockAlreadyHeld = 4,
+ InLockQueue = 5 // lock + notify
};
public:
+
Uint32 senderData;
Uint32 senderRef;
Uint32 lockId;
@@ -122,7 +127,6 @@ public:
Uint32 senderData;
Uint32 senderRef;
Uint32 lockId;
- Uint32 lockKey;
};
class UtilUnlockConf {
@@ -168,7 +172,8 @@ public:
enum ErrorCode {
OK = 0,
NoSuchLock = 1,
- NotLockOwner = 2
+ NotLockOwner = 2,
+ NotInLockQueue = 3
};
public:
Uint32 senderData;
@@ -278,7 +283,6 @@ public:
Uint32 senderData;
Uint32 senderRef;
Uint32 lockId;
- Uint32 lockKey;
};
class UtilDestroyLockRef {
diff -Nrup a/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp
b/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp
--- a/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp 2006-12-23 20:20:11 +01:00
+++ b/storage/ndb/src/common/debugger/signaldata/UtilLock.cpp 2007-11-12 17:13:45 +01:00
@@ -24,6 +24,7 @@ printUTIL_LOCK_REQ (FILE * output, const
fprintf (output, " senderRef: %x\n", sig->senderRef);
fprintf (output, " lockId: %x\n", sig->lockId);
fprintf (output, " requestInfo: %x\n", sig->requestInfo);
+ fprintf (output, " extra: %x\n", sig->extra);
return true;
}
@@ -35,7 +36,6 @@ printUTIL_LOCK_CONF (FILE * output, cons
fprintf (output, " senderData: %x\n", sig->senderData);
fprintf (output, " senderRef: %x\n", sig->senderRef);
fprintf (output, " lockId: %x\n", sig->lockId);
- fprintf (output, " lockKey: %x\n", sig->lockKey);
return true;
}
@@ -59,7 +59,6 @@ printUTIL_UNLOCK_REQ (FILE * output, con
fprintf (output, " senderData: %x\n", sig->senderData);
fprintf (output, " senderRef: %x\n", sig->senderRef);
fprintf (output, " lockId: %x\n", sig->lockId);
- fprintf (output, " lockKey: %x\n", sig->lockKey);
return true;
}
@@ -129,7 +128,6 @@ printUTIL_DESTROY_LOCK_REQ (FILE * outpu
fprintf (output, " senderData: %x\n", sig->senderData);
fprintf (output, " senderRef: %x\n", sig->senderRef);
fprintf (output, " lockId: %x\n", sig->lockId);
- fprintf (output, " lockKey: %x\n", sig->lockKey);
return true;
}
diff -Nrup a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp 2007-09-14 08:04:47 +02:00
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.cpp 2007-11-12 17:13:45 +01:00
@@ -324,11 +324,6 @@ void Dbdict::execCONTINUEB(Signal* signa
sendGetTabResponse(signal);
break;
- case ZDICT_LOCK_POLL:
- jam();
- checkDictLockQueue(signal, true);
- break;
-
default :
ndbrequire(false);
break;
@@ -1562,9 +1557,7 @@ Dbdict::Dbdict(Block_context& ctx):
c_Trans(c_opRecordPool),
c_opCreateObj(c_schemaOp),
c_opDropObj(c_schemaOp),
- c_opRecordSequence(0),
- c_dictLockQueue(c_dictLockPool),
- c_dictLockPoll(false)
+ c_opRecordSequence(0)
{
BLOCK_CONSTRUCTOR(Dbdict);
@@ -1761,7 +1754,6 @@ void Dbdict::initCommonData()
c_numberNode = 0;
c_noNodesFailed = 0;
c_failureNr = 0;
- c_blockState = BS_IDLE;
c_packTable.m_state = PackTable::PTS_IDLE;
c_startPhase = 0;
c_restartType = 255; //Ensure not used restartType
@@ -2105,6 +2097,7 @@ void Dbdict::execREAD_CONFIG_REQ(Signal*
c_obj_pool.setSize(tablerecSize+c_maxNoOfTriggers);
c_obj_hash.setSize((tablerecSize+c_maxNoOfTriggers+1)/2);
+ m_dict_lock_pool.setSize(MAX_NDB_NODES);
Pool_context pc;
pc.m_block = this;
@@ -2130,8 +2123,6 @@ void Dbdict::execREAD_CONFIG_REQ(Signal*
c_opDropTrigger.setSize(8);
c_opAlterTrigger.setSize(8);
- c_dictLockPool.setSize(32);
-
// Initialize schema file copies
c_schemaFile[0].schemaPage =
(SchemaFile*)c_schemaPageRecordArray.getPtr(0 * NDB_SF_MAX_PAGES);
@@ -2663,7 +2654,6 @@ Dbdict::restart_writeSchemaConf(Signal *
}
ndbrequire(c_nodeRestart || c_initialNodeRestart);
- c_blockState = BS_IDLE;
activateIndexes(signal, 0);
return;
}
@@ -3735,39 +3725,25 @@ void Dbdict::execNODE_FAILREP(Signal* si
memcpy(theFailedNodes, nodeFail->theNodes, sizeof(theFailedNodes));
c_counterMgr.execNODE_FAILREP(signal);
-
- bool ok = false;
- switch(c_blockState){
- case BS_IDLE:
+
+ if (masterFailed)
+ {
jam();
- ok = true;
if(c_opRecordPool.getSize() !=
(c_opRecordPool.getNoOfFree() +
c_opSubEvent.get_count() + c_opCreateEvent.get_count() +
c_opDropEvent.get_count() + c_opSignalUtil.get_count()))
{
jam();
- c_blockState = BS_NODE_FAILURE;
+ UtilLockReq lockReq;
+ lockReq.senderRef = reference();
+ lockReq.senderData = 1;
+ lockReq.lockId = 0;
+ lockReq.requestInfo = UtilLockReq::SharedLock;
+ lockReq.extra = DictLockReq::NodeFailureLock;
+ m_dict_lock.lock(m_dict_lock_pool, &lockReq, 0);
}
- break;
- case BS_CREATE_TAB:
- jam();
- ok = true;
- if(!masterFailed)
- break;
- // fall through
- case BS_BUSY:
- case BS_NODE_FAILURE:
- jam();
- c_blockState = BS_NODE_FAILURE;
- ok = true;
- break;
- case BS_NODE_RESTART:
- jam();
- ok = true;
- break;
}
- ndbrequire(ok);
for(unsigned i = 1; i < MAX_NDB_NODES; i++) {
jam();
@@ -3865,18 +3841,6 @@ Dbdict::execCREATE_TABLE_REQ(Signal* sig
break;
}
- if (c_blockState == BS_NODE_RESTART){
- jam();
- parseRecord.errorCode = CreateTableRef::BusyWithNR;
- break;
- }
-
- if (c_blockState != BS_IDLE){
- jam();
- parseRecord.errorCode = CreateTableRef::Busy;
- break;
- }
-
if (checkSingleUserMode(signal->getSendersBlockRef()))
{
jam();
@@ -3892,6 +3856,18 @@ Dbdict::execCREATE_TABLE_REQ(Signal* sig
parseRecord.errorCode = CreateTableRef::Busy;
break;
}
+
+ DictLockReq lockReq;
+ lockReq.userPtr = createTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateTableLock;
+ parseRecord.errorCode = dict_lock_trylock(&lockReq);
+ if (parseRecord.errorCode)
+ {
+ jam();
+ c_opCreateTable.release(createTabPtr);
+ break;
+ }
parseRecord.requestType = DictTabInfo::CreateTableFromAPI;
parseRecord.errorCode = 0;
@@ -3905,6 +3881,7 @@ Dbdict::execCREATE_TABLE_REQ(Signal* sig
if(parseRecord.errorCode != 0){
jam();
+ dict_lock_unlock(0, &lockReq);
c_opCreateTable.release(createTabPtr);
break;
}
@@ -3962,6 +3939,7 @@ Dbdict::execCREATE_TABLE_REQ(Signal* sig
{
jam();
parseRecord.errorCode= signal->theData[0];
+ dict_lock_unlock(0, &lockReq);
c_opCreateTable.release(createTabPtr);
releaseTableObject(parseRecord.tablePtr.i, true);
break;
@@ -3969,7 +3947,6 @@ Dbdict::execCREATE_TABLE_REQ(Signal* sig
createTabPtr.p->key = key;
c_opRecordSequence++;
c_opCreateTable.add(createTabPtr);
- c_blockState = BS_CREATE_TAB;
return;
} while(0);
@@ -4057,18 +4034,6 @@ Dbdict::execALTER_TABLE_REQ(Signal* sign
return;
}
- if(c_blockState == BS_NODE_RESTART){
- jam();
- alterTableRef(signal, req, AlterTableRef::BusyWithNR);
- return;
- }
-
- if(c_blockState != BS_IDLE){
- jam();
- alterTableRef(signal, req, AlterTableRef::Busy);
- return;
- }
-
if (!check_ndb_versions())
{
jam();
@@ -4127,6 +4092,20 @@ Dbdict::execALTER_TABLE_REQ(Signal* sign
return;
}
+ DictLockReq lockReq;
+ lockReq.userPtr = alterTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::AlterTableLock;
+ parseRecord.errorCode = dict_lock_trylock(&lockReq);
+ if (parseRecord.errorCode)
+ {
+ jam();
+ c_opCreateTable.release(alterTabPtr);
+ alterTableRef(signal, req,
+ (AlterTableRef::ErrorCode) parseRecord.errorCode);
+ return;
+ }
+
alterTabPtr.p->m_changeMask = changeMask;
parseRecord.requestType = DictTabInfo::AlterTableFromAPI;
parseRecord.errorCode = 0;
@@ -4139,6 +4118,7 @@ Dbdict::execALTER_TABLE_REQ(Signal* sign
if(parseRecord.errorCode != 0){
jam();
+ dict_lock_unlock(0, &lockReq);
c_opCreateTable.release(alterTabPtr);
alterTableRef(signal, req,
(AlterTableRef::ErrorCode) parseRecord.errorCode,
@@ -4170,9 +4150,6 @@ Dbdict::execALTER_TABLE_REQ(Signal* sign
alterTabPtr.p->m_tabInfoPtrI = tabInfoPtr.i;
- // Alter table on all nodes
- c_blockState = BS_BUSY;
-
Mutex mutex(signal, c_mutexMgr, alterTabPtr.p->m_startLcpMutex);
Callback c = { safe_cast(&Dbdict::alterTable_backup_mutex_locked),
alterTabPtr.p->key };
@@ -4215,8 +4192,14 @@ Dbdict::alterTable_backup_mutex_locked(S
c_tableRecordPool.getPtr(tablePtr, alterTabPtr.p->m_tablePtrI);
releaseTableObject(tablePtr.i, false);
+ DictLockReq lockReq;
+ lockReq.userPtr = alterTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::AlterTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
c_opCreateTable.release(alterTabPtr);
- c_blockState = BS_IDLE;
+
return;
}
@@ -4293,10 +4276,6 @@ Dbdict::execALTER_TAB_REQ(Signal * signa
CreateTableRecordPtr alterTabPtr; // Reuse create table records
- if (senderRef != reference()) {
- jam();
- c_blockState = BS_BUSY;
- }
if ((requestType == AlterTabReq::AlterTablePrepare)
&& (senderRef != reference())) {
jam();
@@ -4539,8 +4518,6 @@ void Dbdict::alterTabRef(Signal * signal
}
sendSignal(senderRef, GSN_ALTER_TAB_REF, signal,
AlterTabRef::SignalLength, JBB);
-
- c_blockState = BS_IDLE;
}
void Dbdict::execALTER_TAB_REF(Signal * signal){
@@ -4574,8 +4551,6 @@ void Dbdict::execALTER_TAB_REF(Signal *
sendSignal(alterTabPtr.p->m_coordinatorRef, GSN_ALTER_TAB_REF, signal,
AlterTabRef::SignalLength, JBB);
- c_blockState = BS_IDLE;
-
return;
}
@@ -4639,7 +4614,6 @@ void Dbdict::execALTER_TAB_REF(Signal *
jam();
sendSignal(senderRef, GSN_ALTER_TABLE_REF, signal,
AlterTableRef::SignalLength, JBB);
- c_blockState = BS_IDLE;
}
else {
jam();
@@ -4899,11 +4873,17 @@ Dbdict::execALTER_TAB_CONF(Signal * sign
}
// Release resources
+
+ DictLockReq lockReq;
+ lockReq.userRef = reference();
+ lockReq.userPtr = alterTabPtr.i;
+ lockReq.lockType = DictLockReq::AlterTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
TableRecordPtr tabPtr;
c_tableRecordPool.getPtr(tabPtr, alterTabPtr.p->m_tablePtrI);
releaseTableObject(tabPtr.i, false);
releaseCreateTableOp(signal,alterTabPtr);
- c_blockState = BS_IDLE;
}
else {
// (!safeCounter.done())
@@ -5194,7 +5174,6 @@ Dbdict::alterTab_writeTableConf(Signal*
c_tableRecordPool.getPtr(tabPtr, alterTabPtr.p->m_tablePtrI);
releaseTableObject(tabPtr.i, false);
releaseCreateTableOp(signal,alterTabPtr);
- c_blockState = BS_IDLE;
}
}
@@ -5384,8 +5363,15 @@ Dbdict::createTab_reply(Signal* signal,
//@todo check api failed
sendSignal(createTabPtr.p->m_senderRef, GSN_CREATE_TABLE_REF, signal,
CreateTableRef::SignalLength, JBB);
+
+ DictLockReq lockReq;
+ lockReq.userPtr = createTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
releaseCreateTableOp(signal,createTabPtr);
- c_blockState = BS_IDLE;
+
return;
}
}
@@ -5443,8 +5429,14 @@ Dbdict::createTab_startLcpMutex_unlocked
//@todo check api failed
sendSignal(createTabPtr.p->m_senderRef, GSN_CREATE_TABLE_CONF, signal,
CreateTableConf::SignalLength, JBB);
+
+ DictLockReq lockReq;
+ lockReq.userPtr = createTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
releaseCreateTableOp(signal,createTabPtr);
- c_blockState = BS_IDLE;
return;
}
@@ -6846,18 +6838,6 @@ Dbdict::execDROP_TABLE_REQ(Signal* signa
return;
}
- if(c_blockState == BS_NODE_RESTART){
- jam();
- dropTableRef(signal, req, DropTableRef::BusyWithNR);
- return;
- }
-
- if(c_blockState != BS_IDLE){
- jam();
- dropTableRef(signal, req, DropTableRef::Busy);
- return;
- }
-
if (checkSingleUserMode(signal->getSendersBlockRef()))
{
jam();
@@ -6909,8 +6889,19 @@ Dbdict::execDROP_TABLE_REQ(Signal* signa
return;
}
- c_blockState = BS_BUSY;
-
+ DictLockReq lockReq;
+ lockReq.userPtr = dropTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropTableLock;
+ Uint32 err = dict_lock_trylock(&lockReq);
+ if (err)
+ {
+ jam();
+ c_opDropTable.release(dropTabPtr);
+ dropTableRef(signal, req, (DropTableRef::ErrorCode)err);
+ return;
+ }
+
dropTabPtr.p->key = ++c_opRecordSequence;
c_opDropTable.add(dropTabPtr);
@@ -6952,7 +6943,13 @@ Dbdict::dropTable_backup_mutex_locked(Si
dropTableRef(signal, &dropTabPtr.p->m_request,
DropTableRef::BackupInProgress);
- c_blockState = BS_IDLE;
+
+ DictLockReq lockReq;
+ lockReq.userPtr = dropTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
c_opDropTable.release(dropTabPtr);
}
else
@@ -7175,8 +7172,13 @@ Dbdict::execDROP_TAB_CONF(Signal* signal
sendSignal(ref, GSN_DROP_TABLE_CONF, signal,
DropTableConf::SignalLength, JBB);
+ DictLockReq lockReq;
+ lockReq.userPtr = dropTabPtr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropTableLock;
+ dict_lock_unlock(signal, &lockReq);
+
c_opDropTable.release(dropTabPtr);
- c_blockState = BS_IDLE;
}
/**
@@ -8075,18 +8077,27 @@ Dbdict::execCREATE_INDX_REQ(Signal* sign
if (getOwnNodeId() != c_masterNodeId) {
jam();
tmperr = CreateIndxRef::NotMaster;
- } else if (c_blockState == BS_NODE_RESTART) {
- jam();
- tmperr = CreateIndxRef::BusyWithNR;
- } else if (c_blockState != BS_IDLE) {
- jam();
- tmperr = CreateIndxRef::Busy;
- }
+ }
else if (checkSingleUserMode(senderRef))
{
jam();
tmperr = CreateIndxRef::SingleUser;
}
+ else
+ {
+ DictLockReq lockReq;
+ lockReq.userPtr = RNIL;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateIndexLock;
+ tmperr = (CreateIndxRef::ErrorCode)dict_lock_trylock(&lockReq);
+
+ if (tmperr == 0)
+ {
+ jam();
+ dict_lock_unlock(0, &lockReq);
+ }
+ }
+
if (tmperr != CreateIndxRef::NoError) {
releaseSections(signal);
OpCreateIndex opBusy;
@@ -8724,22 +8735,32 @@ Dbdict::execDROP_INDX_REQ(Signal* signal
if (getOwnNodeId() != c_masterNodeId) {
jam();
tmperr = DropIndxRef::NotMaster;
- } else if (c_blockState == BS_NODE_RESTART) {
- jam();
- tmperr = DropIndxRef::BusyWithNR;
- } else if (c_blockState != BS_IDLE) {
- jam();
- tmperr = DropIndxRef::Busy;
}
else if (checkSingleUserMode(senderRef))
{
jam();
tmperr = DropIndxRef::SingleUser;
}
+ else
+ {
+ DictLockReq lockReq;
+ lockReq.userPtr = RNIL;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropIndexLock;
+ tmperr = (DropIndxRef::ErrorCode)dict_lock_trylock(&lockReq);
+
+ if (tmperr == 0)
+ {
+ jam();
+ dict_lock_unlock(0, &lockReq);
+ }
+ }
+
if (tmperr != DropIndxRef::NoError) {
err = tmperr;
goto error;
}
+
// forward initial request plus operation key to all
Uint32 indexId= req->getIndexId();
Uint32 indexVersion= req->getIndexVersion();
@@ -10384,13 +10405,15 @@ void Dbdict::execSUB_START_REQ(Signal* s
OpSubEventPtr subbPtr;
Uint32 errCode = 0;
- DictLockPtr loopPtr;
- if (c_dictLockQueue.first(loopPtr) &&
- loopPtr.p->lt->lockType == DictLockReq::NodeRestartLock)
+ LockQueue::Iterator iter;
+ if (m_dict_lock.first(m_dict_lock_pool, iter))
{
- jam();
- errCode = 1405;
- goto busy;
+ if (iter.m_curr.p->m_req.extra == DictLockReq::NodeRestartLock)
+ {
+ jam();
+ errCode = 1405;
+ goto busy;
+ }
}
if (!c_opSubEvent.seize(subbPtr)) {
@@ -13991,7 +14014,16 @@ const Dbdict::DictLockType*
Dbdict::getDictLockType(Uint32 lockType)
{
static const DictLockType lt[] = {
- { DictLockReq::NodeRestartLock, BS_NODE_RESTART, "NodeRestart" }
+ { DictLockReq::NodeRestartLock, "NodeRestart" }
+ ,{ DictLockReq::CreateTableLock, "CreateTable" }
+ ,{ DictLockReq::AlterTableLock, "AlterTable" }
+ ,{ DictLockReq::DropTableLock, "DropTable" }
+ ,{ DictLockReq::CreateIndexLock, "CreateIndex" }
+ ,{ DictLockReq::DropIndexLock, "DropIndex" }
+ ,{ DictLockReq::CreateFileLock, "CreateFile" }
+ ,{ DictLockReq::CreateFilegroupLock, "CreateFilegroup" }
+ ,{ DictLockReq::DropFileLock, "DropFile" }
+ ,{ DictLockReq::DropFilegroupLock, "DropFilegroup" }
};
for (unsigned int i = 0; i < sizeof(lt)/sizeof(lt[0]); i++) {
if ((Uint32) lt[i].lockType == lockType)
@@ -14001,39 +14033,13 @@ Dbdict::getDictLockType(Uint32 lockType)
}
void
-Dbdict::sendDictLockInfoEvent(Uint32 pollCount)
-{
- DictLockPtr loopPtr;
- c_dictLockQueue.first(loopPtr);
- unsigned count = 0;
-
- char queue_buf[100];
- char *p = &queue_buf[0];
- const char *const q = &queue_buf[sizeof(queue_buf)];
- *p = 0;
-
- while (loopPtr.i != RNIL) {
- jam();
- my_snprintf(p, q-p, "%s%u%s",
- ++count == 1 ? "" : " ",
- (unsigned)refToNode(loopPtr.p->req.userRef),
- loopPtr.p->locked ? "L" : "");
- p += strlen(p);
- c_dictLockQueue.next(loopPtr);
- }
-
- infoEvent("DICT: lock bs: %d ops: %d poll: %d cnt: %d queue: %s",
- (int)c_blockState,
- c_opRecordPool.getSize() - c_opRecordPool.getNoOfFree(),
- c_dictLockPoll, (int)pollCount, queue_buf);
-}
-
-void
-Dbdict::sendDictLockInfoEvent(DictLockPtr lockPtr, const char* text)
+Dbdict::sendDictLockInfoEvent(Signal*, const UtilLockReq* req, const char* text)
{
+ const Dbdict::DictLockType* lt = getDictLockType(req->extra);
+
infoEvent("DICT: %s %u for %s",
- text,
- (unsigned)refToNode(lockPtr.p->req.userRef), lockPtr.p->lt->text);
+ text,
+ (unsigned)refToNode(req->senderRef), lt->text);
}
void
@@ -14042,109 +14048,95 @@ Dbdict::execDICT_LOCK_REQ(Signal* signal
jamEntry();
const DictLockReq* req = (const DictLockReq*)&signal->theData[0];
- // make sure bad request crashes slave, not master (us)
+ UtilLockReq lockReq;
+ lockReq.senderRef = req->userRef;
+ lockReq.senderData = req->userPtr;
+ lockReq.lockId = 0;
+ lockReq.requestInfo = 0;
+ lockReq.extra = req->lockType;
- if (getOwnNodeId() != c_masterNodeId) {
+ const DictLockType* lt = getDictLockType(req->lockType);
+
+ if (req->lockType == DictLockReq::NodeRestartLock)
+ {
jam();
- sendDictLockRef(signal, *req, DictLockRef::NotMaster);
- return;
+ lockReq.requestInfo |= UtilLockReq::SharedLock;
}
- const DictLockType* lt = getDictLockType(req->lockType);
- if (lt == NULL) {
+ // make sure bad request crashes slave, not master (us)
+ Uint32 err, res;
+ if (getOwnNodeId() != c_masterNodeId)
+ {
jam();
- sendDictLockRef(signal, *req, DictLockRef::InvalidLockType);
- return;
+ err = DictLockRef::NotMaster;
+ goto ref;
+ }
+
+ if (lt == NULL)
+ {
+ jam();
+ err = DictLockRef::InvalidLockType;
+ goto ref;
}
if (req->userRef != signal->getSendersBlockRef() ||
- getNodeInfo(refToNode(req->userRef)).m_type != NodeInfo::DB) {
+ getNodeInfo(refToNode(req->userRef)).m_type != NodeInfo::DB)
+ {
jam();
- sendDictLockRef(signal, *req, DictLockRef::BadUserRef);
- return;
+ err = DictLockRef::BadUserRef;
+ goto ref;
}
- if (c_aliveNodes.get(refToNode(req->userRef))) {
+ if (c_aliveNodes.get(refToNode(req->userRef)))
+ {
jam();
- sendDictLockRef(signal, *req, DictLockRef::TooLate);
- return;
+ err = DictLockRef::TooLate;
+ goto ref;
}
-
- DictLockPtr lockPtr;
- if (! c_dictLockQueue.seize(lockPtr)) {
+
+ res = m_dict_lock.lock(m_dict_lock_pool, &lockReq, 0);
+ switch(res){
+ case 0:
jam();
- sendDictLockRef(signal, *req, DictLockRef::TooManyRequests);
- return;
+ sendDictLockInfoEvent(signal, &lockReq, "locked by node");
+ goto conf;
+ break;
+ case UtilLockRef::OutOfLockRecords:
+ jam();
+ err = DictLockRef::TooManyRequests;
+ goto ref;
+ break;
+ default:
+ jam();
+ sendDictLockInfoEvent(signal, &lockReq, "lock request by node");
+ break;
}
-
- lockPtr.p->req = *req;
- lockPtr.p->locked = false;
- lockPtr.p->lt = lt;
-
- checkDictLockQueue(signal, false);
-
- if (! lockPtr.p->locked)
- sendDictLockInfoEvent(lockPtr, "lock request by node");
-}
-
-// only table and index ops are checked
-bool
-Dbdict::hasDictLockSchemaOp()
-{
- return
- ! c_opCreateTable.isEmpty() ||
- ! c_opDropTable.isEmpty() ||
- ! c_opCreateIndex.isEmpty() ||
- ! c_opDropIndex.isEmpty();
-}
-
-void
-Dbdict::checkDictLockQueue(Signal* signal, bool poll)
-{
- Uint32 pollCount = ! poll ? 0 : signal->theData[1];
-
- DictLockPtr lockPtr;
-
- do {
- if (! c_dictLockQueue.first(lockPtr)) {
- jam();
- setDictLockPoll(signal, false, pollCount);
- return;
- }
-
- if (lockPtr.p->locked) {
- jam();
- ndbrequire(c_blockState == lockPtr.p->lt->blockState);
- break;
- }
-
- if (hasDictLockSchemaOp()) {
- jam();
- break;
- }
-
- if (c_blockState != BS_IDLE)
- {
- /**
- * If state is BS_NODE_FAILURE, it might be that no op is running
- */
- jam();
- break;
- }
-
- ndbrequire(c_blockState == BS_IDLE);
- lockPtr.p->locked = true;
- c_blockState = lockPtr.p->lt->blockState;
- sendDictLockConf(signal, lockPtr);
-
- sendDictLockInfoEvent(lockPtr, "locked by node");
- } while (0);
-
- // poll while first request is open
- // this routine is called again when it is removed for any reason
-
- bool on = ! lockPtr.p->locked;
- setDictLockPoll(signal, on, pollCount);
+ return;
+
+ref:
+ {
+ DictLockRef* ref = (DictLockRef*)signal->getDataPtrSend();
+ ref->userPtr = lockReq.senderData;
+ ref->lockType = lockReq.extra;
+ ref->errorCode = err;
+
+ sendSignal(lockReq.senderRef, GSN_DICT_LOCK_REF, signal,
+ DictLockRef::SignalLength, JBB);
+ }
+ return;
+
+conf:
+ {
+ DictLockConf* conf = (DictLockConf*)signal->getDataPtrSend();
+
+ conf->userPtr = lockReq.senderData;
+ conf->lockType = lockReq.extra;
+ conf->lockPtr = lockReq.senderData;
+
+ sendSignal(lockReq.senderRef, GSN_DICT_LOCK_CONF, signal,
+ DictLockConf::SignalLength, JBB);
+ }
+ return;
}
void
@@ -14152,128 +14144,157 @@ Dbdict::execDICT_UNLOCK_ORD(Signal* sign
{
jamEntry();
const DictUnlockOrd* ord = (const DictUnlockOrd*)&signal->theData[0];
+
+ DictLockReq req;
+ req.userPtr = ord->senderData;
+ req.userRef = ord->senderRef;
- DictLockPtr lockPtr;
- c_dictLockQueue.getPtr(lockPtr, ord->lockPtr);
- ndbrequire((Uint32) lockPtr.p->lt->lockType == ord->lockType);
-
- if (lockPtr.p->locked) {
+ if (signal->getLength() < DictUnlockOrd::SignalLength)
+ {
jam();
- ndbrequire(c_blockState == lockPtr.p->lt->blockState);
- ndbrequire(! hasDictLockSchemaOp());
- ndbrequire(! c_dictLockQueue.hasPrev(lockPtr));
-
- c_blockState = BS_IDLE;
- sendDictLockInfoEvent(lockPtr, "unlocked by node");
- } else {
- sendDictLockInfoEvent(lockPtr, "lock request removed by node");
+ req.userPtr = ord->lockPtr;
+ req.userRef = signal->getSendersBlockRef();
+ }
+
+ UtilLockReq lockReq;
+ lockReq.senderData = req.userPtr;
+ lockReq.senderRef = req.userRef;
+ lockReq.extra = DictLockReq::NodeRestartLock; // Should check...
+ Uint32 res = dict_lock_unlock(signal, &req);
+ switch(res){
+ case UtilUnlockRef::OK:
+ jam();
+ sendDictLockInfoEvent(signal, &lockReq, "unlocked by node");
+ return;
+ case UtilUnlockRef::NotLockOwner:
+ jam();
+ sendDictLockInfoEvent(signal, &lockReq, "lock request removed by node");
+ return;
+ default:
+ ndbassert(false);
}
-
- c_dictLockQueue.release(lockPtr);
-
- checkDictLockQueue(signal, false);
}
-void
-Dbdict::sendDictLockConf(Signal* signal, DictLockPtr lockPtr)
-{
- DictLockConf* conf = (DictLockConf*)&signal->theData[0];
- const DictLockReq& req = lockPtr.p->req;
-
- conf->userPtr = req.userPtr;
- conf->lockType = req.lockType;
- conf->lockPtr = lockPtr.i;
-
- sendSignal(req.userRef, GSN_DICT_LOCK_CONF, signal,
- DictLockConf::SignalLength, JBB);
-}
+// NF handling
void
-Dbdict::sendDictLockRef(Signal* signal, DictLockReq req, Uint32 errorCode)
+Dbdict::removeStaleDictLocks(Signal* signal, const Uint32* theFailedNodes)
{
- DictLockRef* ref = (DictLockRef*)&signal->theData[0];
-
- ref->userPtr = req.userPtr;
- ref->lockType = req.lockType;
- ref->errorCode = errorCode;
-
- sendSignal(req.userRef, GSN_DICT_LOCK_REF, signal,
- DictLockRef::SignalLength, JBB);
+ LockQueue::Iterator iter;
+ if (m_dict_lock.first(m_dict_lock_pool, iter))
+ {
+ do {
+ if (NodeBitmask::get(theFailedNodes,
+ refToNode(iter.m_curr.p->m_req.senderRef)))
+ {
+ if (iter.m_curr.p->m_req.requestInfo & UtilLockReq::Granted)
+ {
+ jam();
+ sendDictLockInfoEvent(signal, &iter.m_curr.p->m_req,
+ "remove lock by failed node");
+ }
+ else
+ {
+ jam();
+ sendDictLockInfoEvent(signal, &iter.m_curr.p->m_req,
+ "remove lock request by failed node");
+ }
+ DictUnlockOrd* ord = (DictUnlockOrd*)signal->getDataPtrSend();
+ ord->senderRef = iter.m_curr.p->m_req.senderRef;
+ ord->senderData = iter.m_curr.p->m_req.senderData;
+ ord->lockPtr = iter.m_curr.p->m_req.senderData;
+ ord->lockType = iter.m_curr.p->m_req.extra;
+ sendSignal(reference(), GSN_DICT_UNLOCK_ORD, signal,
+ DictUnlockOrd::SignalLength, JBB);
+ }
+ } while (m_dict_lock.next(iter));
+ }
}
-// control polling
-
-void
-Dbdict::setDictLockPoll(Signal* signal, bool on, Uint32 pollCount)
+Uint32
+Dbdict::dict_lock_trylock(const DictLockReq* _req)
{
- if (on) {
+ UtilLockReq req;
+ const UtilLockReq *lockOwner;
+ req.senderData = _req->userPtr;
+ req.senderRef = _req->userRef;
+ req.extra = _req->lockType;
+ req.requestInfo = UtilLockReq::TryLock | UtilLockReq::Notify;
+
+ Uint32 res = m_dict_lock.lock(m_dict_lock_pool, &req, &lockOwner);
+ switch(res){
+ case UtilLockRef::OK:
jam();
- signal->theData[0] = ZDICT_LOCK_POLL;
- signal->theData[1] = pollCount + 1;
- sendSignalWithDelay(reference(), GSN_CONTINUEB, signal, 100, 2);
- }
-
- bool change = (c_dictLockPoll != on);
-
- if (change) {
+ return 0;
+ case UtilLockRef::LockAlreadyHeld:
+ jam();
+ if (lockOwner->extra == DictLockReq::NodeRestartLock)
+ {
+ jam();
+ return CreateTableRef::BusyWithNR;
+ }
+ break;
+ case UtilLockRef::OutOfLockRecords:
+ jam();
+ break;
+ case UtilLockRef::InLockQueue:
jam();
- c_dictLockPoll = on;
+ /**
+ * Should not happen with trylock
+ */
+ ndbassert(false);
+ break;
}
-
- // avoid too many messages if master is stuck busy (BS_NODE_FAILURE)
- bool periodic =
- pollCount < 8 ||
- pollCount < 64 && pollCount % 8 == 0 ||
- pollCount < 512 && pollCount % 64 == 0 ||
- pollCount < 4096 && pollCount % 512 == 0 ||
- pollCount % 4096 == 0; // about every 6 minutes
-
- if (change || periodic)
- sendDictLockInfoEvent(pollCount);
+
+ return CreateTableRef::Busy;
}
-// NF handling
-
-void
-Dbdict::removeStaleDictLocks(Signal* signal, const Uint32* theFailedNodes)
+Uint32
+Dbdict::dict_lock_unlock(Signal* signal, const DictLockReq* _req)
{
- DictLockPtr loopPtr;
- c_dictLockQueue.first(loopPtr);
-
- if (getOwnNodeId() != c_masterNodeId) {
- ndbrequire(loopPtr.i == RNIL);
- return;
+ UtilUnlockReq req;
+ req.senderData = _req->userPtr;
+ req.senderRef = _req->userRef;
+
+ Uint32 res = m_dict_lock.unlock(m_dict_lock_pool, &req);
+ switch(res){
+ case UtilUnlockRef::OK:
+ case UtilUnlockRef::NotLockOwner:
+ break;
+ case UtilUnlockRef::NotInLockQueue:
+ ndbassert(false);
+ return res;
}
- while (loopPtr.i != RNIL) {
- jam();
- DictLockPtr lockPtr = loopPtr;
- c_dictLockQueue.next(loopPtr);
-
- Uint32 nodeId = refToNode(lockPtr.p->req.userRef);
-
- if (NodeBitmask::get(theFailedNodes, nodeId)) {
- if (lockPtr.p->locked) {
+ UtilLockReq lockReq;
+ LockQueue::Iterator iter;
+ if (m_dict_lock.first(m_dict_lock_pool, iter))
+ {
+ int res;
+ while ((res = m_dict_lock.checkLockGrant(iter, &lockReq)) > 0)
+ {
+ jam();
+ /**
+ *
+ */
+ if (res == 2)
+ {
jam();
- ndbrequire(c_blockState == lockPtr.p->lt->blockState);
- ndbrequire(! hasDictLockSchemaOp());
- ndbrequire(! c_dictLockQueue.hasPrev(lockPtr));
-
- c_blockState = BS_IDLE;
-
- sendDictLockInfoEvent(lockPtr, "remove lock by failed node");
- } else {
- sendDictLockInfoEvent(lockPtr, "remove lock request by failed node");
- }
-
- c_dictLockQueue.release(lockPtr);
+ DictLockConf* conf = (DictLockConf*)signal->getDataPtrSend();
+ conf->userPtr = lockReq.senderData;
+ conf->lockPtr = lockReq.senderData;
+ conf->lockType = lockReq.extra;
+ sendSignal(lockReq.senderRef, GSN_DICT_LOCK_CONF, signal,
+ DictLockConf::SignalLength, JBB);
+ }
+
+ m_dict_lock.next(iter);
}
}
- checkDictLockQueue(signal, false);
+ return res;
}
-
/* **************************************************************** */
/* ---------------------------------------------------------------- */
/* MODULE: STORE/RESTORE SCHEMA FILE---------------------- */
@@ -14403,15 +14424,6 @@ Dbdict::execCREATE_FILE_REQ(Signal* sign
break;
}
- if (c_blockState != BS_IDLE){
- jam();
- ref->errorCode = CreateFileRef::Busy;
- ref->status = 0;
- ref->errorKey = 0;
- ref->errorLine = __LINE__;
- break;
- }
-
if (checkSingleUserMode(senderRef))
{
ref->errorCode = CreateFileRef::SingleUser;
@@ -14430,6 +14442,18 @@ Dbdict::execCREATE_FILE_REQ(Signal* sign
ref->errorLine = __LINE__;
break;
}
+
+ DictLockReq lockReq;
+ lockReq.userPtr = trans_ptr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateFileLock;
+ if ((ref->errorCode = dict_lock_trylock(&lockReq)))
+ {
+ jam();
+ ref->errorLine = __LINE__;
+ break;
+ }
+
jam();
const Uint32 trans_key = ++c_opRecordSequence;
trans_ptr.p->key = trans_key;
@@ -14464,6 +14488,8 @@ Dbdict::execCREATE_FILE_REQ(Signal* sign
ref->status = 0;
ref->errorKey = 0;
ref->errorLine = __LINE__;
+
+ dict_lock_unlock(0, &lockReq);
break;
}
@@ -14483,7 +14509,6 @@ Dbdict::execCREATE_FILE_REQ(Signal* sign
sendSignal(rg, GSN_CREATE_OBJ_REQ, signal,
CreateObjReq::SignalLength, JBB);
- c_blockState = BS_CREATE_TAB;
return;
} while(0);
@@ -14519,15 +14544,6 @@ Dbdict::execCREATE_FILEGROUP_REQ(Signal*
break;
}
- if (c_blockState != BS_IDLE){
- jam();
- ref->errorCode = CreateFilegroupRef::Busy;
- ref->status = 0;
- ref->errorKey = 0;
- ref->errorLine = __LINE__;
- break;
- }
-
if (checkSingleUserMode(senderRef))
{
ref->errorCode = CreateFilegroupRef::SingleUser;
@@ -14546,6 +14562,18 @@ Dbdict::execCREATE_FILEGROUP_REQ(Signal*
ref->errorLine = __LINE__;
break;
}
+
+ DictLockReq lockReq;
+ lockReq.userPtr = trans_ptr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::CreateFilegroupLock;
+ if ((ref->errorCode = dict_lock_trylock(&lockReq)))
+ {
+ jam();
+ ref->errorLine = __LINE__;
+ break;
+ }
+
jam();
const Uint32 trans_key = ++c_opRecordSequence;
trans_ptr.p->key = trans_key;
@@ -14577,6 +14605,8 @@ Dbdict::execCREATE_FILEGROUP_REQ(Signal*
ref->status = 0;
ref->errorKey = 0;
ref->errorLine = __LINE__;
+
+ dict_lock_unlock(0, &lockReq);
break;
}
@@ -14596,7 +14626,6 @@ Dbdict::execCREATE_FILEGROUP_REQ(Signal*
sendSignal(rg, GSN_CREATE_OBJ_REQ, signal,
CreateObjReq::SignalLength, JBB);
- c_blockState = BS_CREATE_TAB;
return;
} while(0);
@@ -14632,15 +14661,6 @@ Dbdict::execDROP_FILE_REQ(Signal* signal
break;
}
- if (c_blockState != BS_IDLE)
- {
- jam();
- ref->errorCode = DropFileRef::Busy;
- ref->errorKey = 0;
- ref->errorLine = __LINE__;
- break;
- }
-
if (checkSingleUserMode(senderRef))
{
jam();
@@ -14675,6 +14695,18 @@ Dbdict::execDROP_FILE_REQ(Signal* signal
ref->errorLine = __LINE__;
break;
}
+
+ DictLockReq lockReq;
+ lockReq.userPtr = trans_ptr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropFileLock;
+ if ((ref->errorCode = dict_lock_trylock(&lockReq)))
+ {
+ jam();
+ ref->errorLine = __LINE__;
+ break;
+ }
+
jam();
const Uint32 trans_key = ++c_opRecordSequence;
@@ -14710,7 +14742,6 @@ Dbdict::execDROP_FILE_REQ(Signal* signal
sendSignal(rg, GSN_DROP_OBJ_REQ, signal,
DropObjReq::SignalLength, JBB);
- c_blockState = BS_CREATE_TAB;
return;
} while(0);
@@ -14747,15 +14778,6 @@ Dbdict::execDROP_FILEGROUP_REQ(Signal* s
break;
}
- if (c_blockState != BS_IDLE)
- {
- jam();
- ref->errorCode = DropFilegroupRef::Busy;
- ref->errorKey = 0;
- ref->errorLine = __LINE__;
- break;
- }
-
if (checkSingleUserMode(senderRef))
{
jam();
@@ -14790,6 +14812,18 @@ Dbdict::execDROP_FILEGROUP_REQ(Signal* s
ref->errorLine = __LINE__;
break;
}
+
+ DictLockReq lockReq;
+ lockReq.userPtr = trans_ptr.i;
+ lockReq.userRef = reference();
+ lockReq.lockType = DictLockReq::DropFilegroupLock;
+ if ((ref->errorCode = dict_lock_trylock(&lockReq)))
+ {
+ jam();
+ ref->errorLine = __LINE__;
+ break;
+ }
+
jam();
const Uint32 trans_key = ++c_opRecordSequence;
@@ -14825,7 +14859,6 @@ Dbdict::execDROP_FILEGROUP_REQ(Signal* s
sendSignal(rg, GSN_DROP_OBJ_REQ, signal,
DropObjReq::SignalLength, JBB);
- c_blockState = BS_CREATE_TAB;
return;
} while(0);
@@ -15004,6 +15037,10 @@ Dbdict::trans_commit_complete_done(Signa
ndbrequire(retValue == 0);
ndbrequire(c_Trans.find(trans_ptr, callbackData));
+ DictLockReq lockReq;
+ lockReq.userRef = reference();
+ lockReq.userPtr = trans_ptr.i;
+
switch(f_dict_op[trans_ptr.p->m_op.m_vt_index].m_gsn_user_req){
case GSN_CREATE_FILEGROUP_REQ:{
FilegroupPtr fg_ptr;
@@ -15019,6 +15056,8 @@ Dbdict::trans_commit_complete_done(Signa
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_CREATE_FILEGROUP_CONF, signal,
CreateFilegroupConf::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::CreateFilegroupLock;
break;
}
case GSN_CREATE_FILE_REQ:{
@@ -15034,6 +15073,8 @@ Dbdict::trans_commit_complete_done(Signa
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_CREATE_FILE_CONF, signal,
CreateFileConf::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::CreateFileLock;
break;
}
case GSN_DROP_FILE_REQ:{
@@ -15046,6 +15087,8 @@ Dbdict::trans_commit_complete_done(Signa
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_DROP_FILE_CONF, signal,
DropFileConf::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::DropFileLock;
break;
}
case GSN_DROP_FILEGROUP_REQ:{
@@ -15058,15 +15101,16 @@ Dbdict::trans_commit_complete_done(Signa
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_DROP_FILEGROUP_CONF, signal,
DropFilegroupConf::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::DropFilegroupLock;
break;
}
default:
ndbrequire(false);
}
+ dict_lock_unlock(signal, &lockReq);
c_Trans.release(trans_ptr);
- ndbrequire(c_blockState == BS_CREATE_TAB);
- c_blockState = BS_IDLE;
return;
}
@@ -15104,6 +15148,10 @@ Dbdict::trans_abort_complete_done(Signal
ndbrequire(retValue == 0);
ndbrequire(c_Trans.find(trans_ptr, callbackData));
+ DictLockReq lockReq;
+ lockReq.userRef = reference();
+ lockReq.userPtr = trans_ptr.i;
+
switch(f_dict_op[trans_ptr.p->m_op.m_vt_index].m_gsn_user_req){
case GSN_CREATE_FILEGROUP_REQ:
{
@@ -15117,10 +15165,12 @@ Dbdict::trans_abort_complete_done(Signal
ref->errorLine = 0;
ref->errorKey = 0;
ref->status = 0;
-
+
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_CREATE_FILEGROUP_REF, signal,
CreateFilegroupRef::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::CreateFilegroupLock;
break;
}
case GSN_CREATE_FILE_REQ:
@@ -15138,6 +15188,8 @@ Dbdict::trans_abort_complete_done(Signal
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_CREATE_FILE_REF, signal,
CreateFileRef::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::CreateFileLock;
break;
}
case GSN_DROP_FILE_REQ:
@@ -15154,6 +15206,8 @@ Dbdict::trans_abort_complete_done(Signal
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_DROP_FILE_REF, signal,
DropFileRef::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::DropFileLock;
break;
}
case GSN_DROP_FILEGROUP_REQ:
@@ -15171,15 +15225,16 @@ Dbdict::trans_abort_complete_done(Signal
//@todo check api failed
sendSignal(trans_ptr.p->m_senderRef, GSN_DROP_FILEGROUP_REF, signal,
DropFilegroupRef::SignalLength, JBB);
+
+ lockReq.lockType = DictLockReq::DropFilegroupLock;
break;
}
default:
ndbrequire(false);
}
+ dict_lock_unlock(signal, &lockReq);
c_Trans.release(trans_ptr);
- ndbrequire(c_blockState == BS_CREATE_TAB);
- c_blockState = BS_IDLE;
return;
}
diff -Nrup a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp
--- a/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp 2007-05-15 09:13:18 +02:00
+++ b/storage/ndb/src/kernel/blocks/dbdict/Dbdict.hpp 2007-11-12 17:13:46 +01:00
@@ -60,6 +60,7 @@
#include <signaldata/DictObjOp.hpp>
#include <signaldata/DropFilegroupImpl.hpp>
#include <SLList.hpp>
+#include <LockQueue.hpp>
#ifdef DBDICT_C
// Debug Macros
@@ -69,7 +70,6 @@
/*--------------------------------------------------------------*/
#define ZPACK_TABLE_INTO_PAGES 0
#define ZSEND_GET_TAB_RESPONSE 3
-#define ZDICT_LOCK_POLL 4
/*--------------------------------------------------------------*/
@@ -1059,46 +1059,6 @@ private:
/* ----------------------------------------------------------------------- */
// State variables
/* ----------------------------------------------------------------------- */
-
-#ifndef ndb_dbdict_log_block_state
- enum BlockState {
- BS_IDLE = 0,
- BS_CREATE_TAB = 1,
- BS_BUSY = 2,
- BS_NODE_FAILURE = 3,
- BS_NODE_RESTART = 4
- };
-#else // quick hack to log changes
- enum {
- BS_IDLE = 0,
- BS_CREATE_TAB = 1,
- BS_BUSY = 2,
- BS_NODE_FAILURE = 3,
- BS_NODE_RESTART = 4
- };
- struct BlockState;
- friend struct BlockState;
- struct BlockState {
- BlockState() :
- m_value(BS_IDLE) {
- }
- BlockState(int value) :
- m_value(value) {
- }
- operator int() const {
- return m_value;
- }
- BlockState& operator=(const BlockState& bs) {
- Dbdict* dict = (Dbdict*)globalData.getBlock(DBDICT);
- dict->infoEvent("DICT: bs %d->%d", m_value, bs.m_value);
- globalSignalLoggers.log(DBDICT, "bs %d->%d", m_value, bs.m_value);
- m_value = bs.m_value;
- return *this;
- }
- int m_value;
- };
-#endif
- BlockState c_blockState;
struct PackTable {
@@ -2072,73 +2032,6 @@ private:
// Unique key for operation XXX move to some system table
Uint32 c_opRecordSequence;
- /*
- * Master DICT can be locked in 2 mutually exclusive ways:
- *
- * 1) for schema ops, via operation records
- * 2) against schema ops, via a lock queue
- *
- * Current use of 2) is by a starting node, to prevent schema ops
- * until started. The ops are refused (BlockState != BS_IDLE),
- * not queued.
- *
- * Master failure is not handled, in node start case the starting
- * node will crash too anyway. Use lock table in future..
- *
- * The lock queue is "serial" but other behaviour is possible
- * by checking lock types e.g. to allow parallel node starts.
- *
- * Checking release of last op record is not convenient with
- * current structure (5.0). Instead we poll via continueB.
- *
- * XXX only table ops check BlockState
- */
- struct DictLockType;
- friend struct DictLockType;
-
- struct DictLockType {
- DictLockReq::LockType lockType;
- BlockState blockState;
- const char* text;
- };
-
- struct DictLockRecord;
- friend struct DictLockRecord;
-
- struct DictLockRecord {
- DictLockReq req;
- const DictLockType* lt;
- bool locked;
- union {
- Uint32 nextPool;
- Uint32 nextList;
- };
- Uint32 prevList;
- };
-
- typedef Ptr<DictLockRecord> DictLockPtr;
- ArrayPool<DictLockRecord> c_dictLockPool;
- DLFifoList<DictLockRecord> c_dictLockQueue;
- bool c_dictLockPoll;
-
- static const DictLockType* getDictLockType(Uint32 lockType);
- void sendDictLockInfoEvent(Uint32 pollCount);
- void sendDictLockInfoEvent(DictLockPtr lockPtr, const char* text);
-
- // check if any schema op exists (conflicting with dict lock)
- bool hasDictLockSchemaOp();
-
- void checkDictLockQueue(Signal* signal, bool poll);
- void sendDictLockConf(Signal* signal, DictLockPtr lockPtr);
- void sendDictLockRef(Signal* signal, DictLockReq req, Uint32 errorCode);
-
- // control polling i.e. continueB loop
- void setDictLockPoll(Signal* signal, bool on, Uint32 pollCount);
-
- // NF handling
- void removeStaleDictLocks(Signal* signal, const Uint32* theFailedNodes);
-
-
// Statement blocks
/* ------------------------------------------------------------ */
@@ -2668,6 +2561,37 @@ public:
void drop_undofile_commit_complete(Signal* signal, SchemaOp*);
int checkSingleUserMode(Uint32 senderRef);
+
+
+ /**
+ * Dict lock queue does currently uniformly handle
+ *
+ * - starting node
+ * - schema op
+ *
+ * The impl. is based on DbUtil lock's (LockQueue)
+ *
+ * It would be very nice to use this *fully*
+ * But instead of introducing extra break in schema-op
+ * a lock queue in instantiated in Dict, for easy trylock-handling
+ */
+ struct DictLockType;
+ friend struct DictLockType;
+
+ struct DictLockType {
+ DictLockReq::LockType lockType;
+ const char* text;
+ };
+ static const DictLockType* getDictLockType(Uint32 lockType);
+ void sendDictLockInfoEvent(Signal*, const UtilLockReq*, const char* text);
+ void removeStaleDictLocks(Signal* signal, const Uint32* theFailedNodes);
+
+
+ Uint32 dict_lock_trylock(const DictLockReq* req);
+ Uint32 dict_lock_unlock(Signal* signal, const DictLockReq* req);
+
+ LockQueue::Pool m_dict_lock_pool;
+ LockQueue m_dict_lock;
};
inline bool
diff -Nrup a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp
--- a/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp 2007-11-08 10:20:09 +01:00
+++ b/storage/ndb/src/kernel/blocks/dbdih/DbdihMain.cpp 2007-11-12 17:13:46 +01:00
@@ -16390,6 +16390,8 @@ Dbdih::sendDictUnlockOrd(Signal* signal,
ord->lockPtr = lockPtr.p->lockPtr;
ord->lockType = lockPtr.p->lockType;
+ ord->senderData = lockPtr.i;
+ ord->senderRef = reference();
c_dictLockSlavePool.release(lockPtr);
diff -Nrup a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp
--- a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp 2007-10-25 10:59:16 +02:00
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.cpp 2007-11-12 17:13:46 +01:00
@@ -622,7 +622,7 @@ DbUtil::execDUMP_STATE_ORD(Signal* signa
ptr.p->m_mutexId = signal->theData[1];
Callback c = { safe_cast(&DbUtil::mutex_locked), ptr.i };
ptr.p->m_callback = c;
- c_mutexMgr.lock(signal, ptr);
+ c_mutexMgr.lock(signal, ptr, true);
ndbout_c("c_mutexMgr.lock ptrI=%d mutexId=%d", ptr.i, ptr.p->m_mutexId);
}
@@ -640,12 +640,56 @@ DbUtil::execDUMP_STATE_ORD(Signal* signa
MutexManager::ActiveMutexPtr ptr;
ndbrequire(c_mutexMgr.seize(ptr));
ptr.p->m_mutexId = signal->theData[1];
- ptr.p->m_mutexKey = signal->theData[2];
Callback c = { safe_cast(&DbUtil::mutex_destroyed), ptr.i };
ptr.p->m_callback = c;
c_mutexMgr.destroy(signal, ptr);
- ndbout_c("c_mutexMgr.destroy ptrI=%d mutexId=%d key=%d",
- ptr.i, ptr.p->m_mutexId, ptr.p->m_mutexKey);
+ ndbout_c("c_mutexMgr.destroy ptrI=%d mutexId=%d",
+ ptr.i, ptr.p->m_mutexId);
+ }
+
+ if (tCase == 244)
+ {
+ jam();
+ DLHashTable<LockQueueInstance>::Iterator iter;
+ Uint32 bucket = signal->theData[1];
+ if (signal->getLength() == 1)
+ {
+ bucket = 0;
+ infoEvent("Starting dumping of DbUtil::Locks");
+ }
+ c_lockQueues.next(bucket, iter);
+
+ for (Uint32 i = 0; i<32 || iter.bucket == bucket; i++)
+ {
+ if (iter.curr.isNull())
+ {
+ infoEvent("Dumping of DbUtil::Locks - done");
+ return;
+ }
+
+ infoEvent("LockQueue %u", iter.curr.p->m_lockId);
+
+ LockQueue::Iterator iter2;
+ if (iter.curr.p->m_queue.first(c_lockElementPool, iter2))
+ {
+ do
+ {
+ Ptr<LockQueue::LockQueueElement> ptr = iter2.m_curr;
+ infoEvent("- sender: 0x%x data: %u %s %s",
+ ptr.p->m_req.senderRef,
+ ptr.p->m_req.senderData,
+ (ptr.p->m_req.requestInfo & UtilLockReq::SharedLock) ?
+ "S":"X",
+ (ptr.p->m_req.requestInfo & UtilLockReq::Granted) ?
+ "granted" : "");
+ } while (iter.curr.p->m_queue.next(iter2));
+ }
+ c_lockQueues.next(iter);
+ }
+ signal->theData[0] = 244;
+ signal->theData[1] = iter.bucket;
+ sendSignal(reference(), GSN_DUMP_STATE_ORD, signal, 2, JBB);
+ return;
}
}
@@ -671,8 +715,8 @@ void
DbUtil::mutex_locked(Signal* signal, Uint32 ptrI, Uint32 retVal){
MutexManager::ActiveMutexPtr ptr; ptr.i = ptrI;
c_mutexMgr.getPtr(ptr);
- ndbout_c("mutex_locked - mutexId=%d, retVal=%d key=%d ptrI=%d",
- ptr.p->m_mutexId, retVal, ptr.p->m_mutexKey, ptrI);
+ ndbout_c("mutex_locked - mutexId=%d, retVal=%d ptrI=%d",
+ ptr.p->m_mutexId, retVal, ptrI);
if(retVal)
c_mutexMgr.release(ptrI);
}
@@ -2335,92 +2379,122 @@ DbUtil::finishTransaction(Signal* signal
void
DbUtil::execUTIL_LOCK_REQ(Signal * signal){
jamEntry();
- UtilLockReq * req = (UtilLockReq*)signal->getDataPtr();
- const Uint32 lockId = req->lockId;
+
+ UtilLockReq req = *(UtilLockReq*)signal->getDataPtr();
LockQueuePtr lockQPtr;
- if(!c_lockQueues.find(lockQPtr, lockId)){
+ if(!c_lockQueues.find(lockQPtr, req.lockId))
+ {
jam();
- sendLOCK_REF(signal, req, UtilLockRef::NoSuchLock);
+ sendLOCK_REF(signal, &req, UtilLockRef::NoSuchLock);
return;
}
-// const Uint32 requestInfo = req->requestInfo;
- const Uint32 senderNode = refToNode(req->senderRef);
- if(senderNode != getOwnNodeId() && senderNode != 0){
+ const Uint32 senderNode = refToNode(req.senderRef);
+ if(senderNode != getOwnNodeId() && senderNode != 0)
+ {
jam();
- sendLOCK_REF(signal, req, UtilLockRef::DistributedLockNotSupported);
+ sendLOCK_REF(signal, &req, UtilLockRef::DistributedLockNotSupported);
return;
}
-
- LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
- lockQPtr.p->m_queue);
- if(req->requestInfo & UtilLockReq::TryLock && !queue.isEmpty()){
+
+ Uint32 res = lockQPtr.p->m_queue.lock(c_lockElementPool, &req);
+ switch(res){
+ case UtilLockRef::OK:
jam();
- sendLOCK_REF(signal, req, UtilLockRef::LockAlreadyHeld);
+ sendLOCK_CONF(signal, &req);
return;
- }
-
- LockQueueElementPtr lockEPtr;
- if(!c_lockElementPool.seize(lockEPtr)){
+ case UtilLockRef::OutOfLockRecords:
jam();
- sendLOCK_REF(signal, req, UtilLockRef::OutOfLockRecords);
+ sendLOCK_REF(signal, &req, UtilLockRef::OutOfLockRecords);
return;
- }
-
- lockEPtr.p->m_senderRef = req->senderRef;
- lockEPtr.p->m_senderData = req->senderData;
-
- if(queue.isEmpty()){
+ case UtilLockRef::InLockQueue:
+ jam();
+ if (req.requestInfo & UtilLockReq::Notify)
+ {
+ jam();
+ sendLOCK_REF(signal, &req, UtilLockRef::InLockQueue);
+ }
+ return;
+ case UtilLockRef::LockAlreadyHeld:
jam();
- sendLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
+ ndbassert(req.requestInfo & UtilLockReq::TryLock);
+ sendLOCK_REF(signal, &req, UtilLockRef::LockAlreadyHeld);
+ return;
+ default:
+ jam();
+ ndbassert(false);
+ sendLOCK_REF(signal, &req, (UtilLockRef::ErrorCode)res);
+ return;
}
-
- queue.add(lockEPtr);
}
void
-DbUtil::execUTIL_UNLOCK_REQ(Signal* signal){
+DbUtil::execUTIL_UNLOCK_REQ(Signal* signal)
+{
jamEntry();
- UtilUnlockReq * req = (UtilUnlockReq*)signal->getDataPtr();
- const Uint32 lockId = req->lockId;
+ UtilUnlockReq req = *(UtilUnlockReq*)signal->getDataPtr();
LockQueuePtr lockQPtr;
- if(!c_lockQueues.find(lockQPtr, lockId)){
+ if(!c_lockQueues.find(lockQPtr, req.lockId))
+ {
jam();
- sendUNLOCK_REF(signal, req, UtilUnlockRef::NoSuchLock);
+ sendUNLOCK_REF(signal, &req, UtilUnlockRef::NoSuchLock);
return;
}
- LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
- lockQPtr.p->m_queue);
- LockQueueElementPtr lockEPtr;
- if(!queue.first(lockEPtr)){
+ Uint32 res = lockQPtr.p->m_queue.unlock(c_lockElementPool, &req);
+ switch(res){
+ case UtilUnlockRef::OK:
+ case UtilUnlockRef::NotLockOwner: {
jam();
- sendUNLOCK_REF(signal, req, UtilUnlockRef::NotLockOwner);
- return;
+ UtilUnlockConf * conf = (UtilUnlockConf*)signal->getDataPtrSend();
+ conf->senderData = req.senderData;
+ conf->senderRef = reference();
+ conf->lockId = req.lockId;
+ sendSignal(req.senderRef, GSN_UTIL_UNLOCK_CONF, signal,
+ UtilUnlockConf::SignalLength, JBB);
+ break;
}
-
- if(lockQPtr.p->m_lockKey != req->lockKey){
+ case UtilUnlockRef::NotInLockQueue:
jam();
- sendUNLOCK_REF(signal, req, UtilUnlockRef::NotLockOwner);
- return;
+ default:
+ jam();
+ ndbassert(false);
+ sendUNLOCK_REF(signal, &req, (UtilUnlockRef::ErrorCode)res);
+ break;
}
-
- sendUNLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
- queue.release(lockEPtr);
- if(queue.first(lockEPtr)){
- jam();
- sendLOCK_CONF(signal, lockQPtr.p, lockEPtr.p);
- return;
+ /**
+ * Unlock can make other(s) acquie lock
+ */
+ UtilLockReq lockReq;
+ LockQueue::Iterator iter;
+ if (lockQPtr.p->m_queue.first(c_lockElementPool, iter))
+ {
+ int res;
+ while ((res = lockQPtr.p->m_queue.checkLockGrant(iter, &lockReq)) > 0)
+ {
+ jam();
+ /**
+ *
+ */
+ if (res == 2)
+ {
+ jam();
+ sendLOCK_CONF(signal, &lockReq);
+ }
+
+ lockQPtr.p->m_queue.next(iter);
+ }
}
}
void
DbUtil::sendLOCK_REF(Signal* signal,
- const UtilLockReq * req, UtilLockRef::ErrorCode err){
+ const UtilLockReq * req, UtilLockRef::ErrorCode err)
+{
const Uint32 senderData = req->senderData;
const Uint32 senderRef = req->senderRef;
const Uint32 lockId = req->lockId;
@@ -2435,20 +2509,17 @@ DbUtil::sendLOCK_REF(Signal* signal,
}
void
-DbUtil::sendLOCK_CONF(Signal* signal,
- LockQueue * lockQP,
- LockQueueElement * lockEP){
- const Uint32 senderData = lockEP->m_senderData;
- const Uint32 senderRef = lockEP->m_senderRef;
- const Uint32 lockId = lockQP->m_lockId;
- const Uint32 lockKey = ++lockQP->m_lockKey;
+DbUtil::sendLOCK_CONF(Signal* signal, const UtilLockReq * req)
+{
+ const Uint32 senderData = req->senderData;
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 lockId = req->lockId;
UtilLockConf * conf = (UtilLockConf*)signal->getDataPtrSend();
conf->senderData = senderData;
conf->senderRef = reference();
conf->lockId = lockId;
- conf->lockKey = lockKey;
- sendSignal(senderRef, GSN_UTIL_LOCK_CONF, signal,
+ sendSignal(senderRef, GSN_UTIL_LOCK_CONF, signal,
UtilLockConf::SignalLength, JBB);
}
@@ -2469,23 +2540,6 @@ DbUtil::sendUNLOCK_REF(Signal* signal,
UtilUnlockRef::SignalLength, JBB);
}
-void
-DbUtil::sendUNLOCK_CONF(Signal* signal,
- LockQueue * lockQP,
- LockQueueElement * lockEP){
- const Uint32 senderData = lockEP->m_senderData;
- const Uint32 senderRef = lockEP->m_senderRef;
- const Uint32 lockId = lockQP->m_lockId;
- ++lockQP->m_lockKey;
-
- UtilUnlockConf * conf = (UtilUnlockConf*)signal->getDataPtrSend();
- conf->senderData = senderData;
- conf->senderRef = reference();
- conf->lockId = lockId;
- sendSignal(senderRef, GSN_UTIL_UNLOCK_CONF, signal,
- UtilUnlockConf::SignalLength, JBB);
-}
-
void
DbUtil::execUTIL_CREATE_LOCK_REQ(Signal* signal){
jamEntry();
@@ -2513,7 +2567,7 @@ DbUtil::execUTIL_CREATE_LOCK_REQ(Signal*
break;
}
- new (lockQPtr.p) LockQueue(req.lockId);
+ new (lockQPtr.p) LockQueueInstance(req.lockId);
c_lockQueues.add(lockQPtr);
UtilCreateLockConf * conf = (UtilCreateLockConf*)signal->getDataPtrSend();
@@ -2544,22 +2598,26 @@ DbUtil::execUTIL_DESTORY_LOCK_REQ(Signal
UtilDestroyLockRef::ErrorCode err = UtilDestroyLockRef::OK;
do {
LockQueuePtr lockQPtr;
- if(!c_lockQueues.find(lockQPtr, req.lockId)){
+ if(!c_lockQueues.find(lockQPtr, req.lockId))
+ {
jam();
err = UtilDestroyLockRef::NoSuchLock;
break;
}
- LocalDLFifoList<LockQueueElement> queue(c_lockElementPool,
- lockQPtr.p->m_queue);
- LockQueueElementPtr lockEPtr;
- if(!queue.first(lockEPtr)){
+ LockQueue::Iterator iter;
+ if (lockQPtr.p->m_queue.first(c_lockElementPool, iter) == false)
+ {
jam();
err = UtilDestroyLockRef::NotLockOwner;
break;
}
- if(lockQPtr.p->m_lockKey != req.lockKey){
+ if (! (iter.m_curr.p->m_req.senderData == req.senderData &&
+ iter.m_curr.p->m_req.senderRef == req.senderRef &&
+ (! (iter.m_curr.p->m_req.requestInfo & UtilLockReq::SharedLock))
&&
+ iter.m_curr.p->m_req.requestInfo & UtilLockReq::Granted))
+ {
jam();
err = UtilDestroyLockRef::NotLockOwner;
break;
@@ -2568,21 +2626,14 @@ DbUtil::execUTIL_DESTORY_LOCK_REQ(Signal
/**
* OK
*/
-
- // Inform all in lock queue that queue has been destroyed
- UtilLockRef * ref = (UtilLockRef*)signal->getDataPtrSend();
- ref->lockId = req.lockId;
- ref->errorCode = UtilLockRef::NoSuchLock;
- ref->senderRef = reference();
- LockQueueElementPtr loopPtr = lockEPtr;
- for(queue.next(loopPtr); !loopPtr.isNull(); queue.next(loopPtr)){
+
+ while (lockQPtr.p->m_queue.next(iter))
+ {
jam();
- ref->senderData = loopPtr.p->m_senderData;
- const Uint32 senderRef = loopPtr.p->m_senderRef;
- sendSignal(senderRef, GSN_UTIL_LOCK_REF, signal,
- UtilLockRef::SignalLength, JBB);
+ sendLOCK_REF(signal, &iter.m_curr.p->m_req, UtilLockRef::NoSuchLock);
}
- queue.release();
+
+ lockQPtr.p->m_queue.clear(c_lockElementPool);
c_lockQueues.release(lockQPtr);
// Send Destroy conf
@@ -2594,7 +2645,7 @@ DbUtil::execUTIL_DESTORY_LOCK_REQ(Signal
UtilDestroyLockConf::SignalLength, JBB);
return;
} while(false);
-
+
UtilDestroyLockRef * ref = (UtilDestroyLockRef*)signal->getDataPtrSend();
ref->senderData = req.senderData;
ref->senderRef = reference();
diff -Nrup a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp
b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp
--- a/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp 2006-12-23 20:20:17 +01:00
+++ b/storage/ndb/src/kernel/blocks/dbutil/DbUtil.hpp 2007-11-12 17:13:46 +01:00
@@ -36,6 +36,8 @@
#include <SimpleProperties.hpp>
#include <Array.hpp>
+#include <LockQueue.hpp>
+
#define UTIL_WORDS_PER_PAGE 1023
/**
@@ -423,26 +425,15 @@ public:
/***************************************************************************
* Lock manager
*/
- struct LockQueueElement {
- Uint32 m_senderData;
- Uint32 m_senderRef;
- union {
- Uint32 nextPool;
- Uint32 nextList;
- };
- Uint32 prevList;
- };
- typedef Ptr<LockQueueElement> LockQueueElementPtr;
-
- struct LockQueue {
- LockQueue(){}
- LockQueue(Uint32 id) : m_queue() { m_lockId = id; m_lockKey = 0;}
+ struct LockQueueInstance {
+ LockQueueInstance(){}
+ LockQueueInstance(Uint32 id) : m_queue() { m_lockId = id; }
union {
Uint32 m_lockId;
Uint32 key;
};
- Uint32 m_lockKey;
- DLFifoList<LockQueueElement>::Head m_queue;
+
+ LockQueue m_queue;
union {
Uint32 nextHash;
Uint32 nextPool;
@@ -452,16 +443,15 @@ public:
Uint32 hashValue() const {
return m_lockId;
}
- bool equal(const LockQueue & rec) const {
+ bool equal(const LockQueueInstance & rec) const {
return m_lockId == rec.m_lockId;
}
};
- typedef Ptr<LockQueue> LockQueuePtr;
-
+ typedef Ptr<LockQueueInstance> LockQueuePtr;
- ArrayPool<LockQueue> c_lockQueuePool;
- ArrayPool<LockQueueElement> c_lockElementPool;
- KeyTable<LockQueue> c_lockQueues;
+ ArrayPool<LockQueueInstance> c_lockQueuePool;
+ KeyTable<LockQueueInstance> c_lockQueues;
+ LockQueue::Pool c_lockElementPool;
void execUTIL_CREATE_LOCK_REQ(Signal* signal);
void execUTIL_DESTORY_LOCK_REQ(Signal* signal);
@@ -469,10 +459,9 @@ public:
void execUTIL_UNLOCK_REQ(Signal* signal);
void sendLOCK_REF(Signal*, const UtilLockReq * req, UtilLockRef::ErrorCode);
- void sendLOCK_CONF(Signal*, LockQueue *, LockQueueElement *);
+ void sendLOCK_CONF(Signal*, const UtilLockReq * req);
void sendUNLOCK_REF(Signal*, const UtilUnlockReq*, UtilUnlockRef::ErrorCode);
- void sendUNLOCK_CONF(Signal*, LockQueue *, LockQueueElement *);
// For testing of mutex:es
void mutex_created(Signal* signal, Uint32 mutexId, Uint32 retVal);
diff -Nrup a/storage/ndb/src/kernel/vm/LockQueue.cpp
b/storage/ndb/src/kernel/vm/LockQueue.cpp
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/storage/ndb/src/kernel/vm/LockQueue.cpp 2007-11-12 17:13:46 +01:00
@@ -0,0 +1,206 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "LockQueue.hpp"
+
+Uint32
+LockQueue::lock(Pool & thePool,
+ const UtilLockReq* req, const UtilLockReq** lockOwner)
+{
+ const bool exclusive = ! (req->requestInfo & UtilLockReq::SharedLock);
+ const bool trylock = req->requestInfo & UtilLockReq::TryLock;
+ const bool notify = req->requestInfo & UtilLockReq::Notify;
+
+ LocalDLFifoList<LockQueueElement> queue(thePool, m_queue);
+
+ bool grant = true;
+ Ptr<LockQueueElement> lockEPtr;
+ if (queue.last(lockEPtr))
+ {
+ jam();
+ if (! (lockEPtr.p->m_req.requestInfo & UtilLockReq::SharedLock))
+ {
+ jam();
+ grant = false;
+ }
+ else if (exclusive)
+ {
+ jam();
+ grant = false;
+ }
+ else if (lockEPtr.p->m_req.requestInfo & UtilLockReq::Granted)
+ {
+ jam();
+ grant = true;
+ }
+ else
+ {
+ jam();
+ grant = false;
+ }
+ }
+
+ if(trylock && grant == false)
+ {
+ jam();
+ if (notify && lockOwner)
+ {
+ jam();
+ queue.first(lockEPtr);
+ * lockOwner = &lockEPtr.p->m_req;
+ }
+ return UtilLockRef::LockAlreadyHeld;
+ }
+
+ if(!thePool.seize(lockEPtr))
+ {
+ jam();
+ return UtilLockRef::OutOfLockRecords;
+ }
+
+ lockEPtr.p->m_req = *req;
+
+ int retVal = UtilLockRef::OK;
+ if(grant)
+ {
+ jam();
+ lockEPtr.p->m_req.requestInfo |= UtilLockReq::Granted;
+ }
+ else if (notify)
+ {
+ jam();
+ retVal = UtilLockRef::InLockQueue;
+ }
+
+ queue.addLast(lockEPtr);
+
+ return retVal;
+}
+
+Uint32
+LockQueue::unlock(Pool & thePool,
+ const UtilUnlockReq* req)
+{
+ const Uint32 senderRef = req->senderRef;
+ const Uint32 senderData = req->senderData;
+
+ Ptr<LockQueueElement> lockEPtr;
+ LocalDLFifoList<LockQueueElement> queue(thePool, m_queue);
+
+ for (queue.first(lockEPtr); !lockEPtr.isNull(); queue.next(lockEPtr))
+ {
+ jam();
+ if (lockEPtr.p->m_req.senderData == senderData &&
+ lockEPtr.p->m_req.senderRef == senderRef)
+ {
+ jam();
+
+ Uint32 res;
+ if (lockEPtr.p->m_req.requestInfo & UtilLockReq::Granted)
+ {
+ jam();
+ res = UtilUnlockRef::OK;
+ }
+ else
+ {
+ jam();
+ res = UtilUnlockRef::NotLockOwner;
+ }
+ queue.release(lockEPtr);
+ return res;
+ }
+ }
+
+ return UtilUnlockRef::NotInLockQueue;
+}
+
+bool
+LockQueue::first(Pool& thePool, Iterator & iter)
+{
+ LocalDLFifoList<LockQueueElement> queue(thePool, m_queue);
+ if (queue.first(iter.m_curr))
+ {
+ iter.m_prev.setNull();
+ iter.thePool = &thePool;
+ return true;
+ }
+ return false;
+}
+
+bool
+LockQueue::next(Iterator& iter)
+{
+ iter.m_prev = iter.m_curr;
+ LocalDLFifoList<LockQueueElement> queue(*iter.thePool, m_queue);
+ return queue.next(iter.m_curr);
+}
+
+int
+LockQueue::checkLockGrant(Iterator& iter, UtilLockReq* req)
+{
+ LocalDLFifoList<LockQueueElement> queue(*iter.thePool, m_queue);
+ if (iter.m_prev.isNull())
+ {
+ if (iter.m_curr.p->m_req.requestInfo & UtilLockReq::Granted)
+ {
+ jam();
+ return 1;
+ }
+ else
+ {
+ jam();
+ * req = iter.m_curr.p->m_req;
+ iter.m_curr.p->m_req.requestInfo |= UtilLockReq::Granted;
+ return 2;
+ }
+ }
+ else
+ {
+ jam();
+ /**
+ * Prev is granted...
+ */
+ assert(iter.m_prev.p->m_req.requestInfo & UtilLockReq::Granted);
+ if (iter.m_prev.p->m_req.requestInfo & UtilLockReq::SharedLock)
+ {
+ jam();
+ if (iter.m_curr.p->m_req.requestInfo & UtilLockReq::SharedLock)
+ {
+ jam();
+ if (iter.m_curr.p->m_req.requestInfo & UtilLockReq::Granted)
+ {
+ jam();
+ return 1;
+ }
+ else
+ {
+ jam();
+ * req = iter.m_curr.p->m_req;
+ iter.m_curr.p->m_req.requestInfo |= UtilLockReq::Granted;
+ return 2;
+ }
+ }
+ }
+ return 0;
+ }
+}
+
+void
+LockQueue::clear(Pool& thePool)
+{
+ LocalDLFifoList<LockQueueElement> queue(thePool, m_queue);
+ queue.release();
+}
diff -Nrup a/storage/ndb/src/kernel/vm/LockQueue.hpp
b/storage/ndb/src/kernel/vm/LockQueue.hpp
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/storage/ndb/src/kernel/vm/LockQueue.hpp 2007-11-12 17:13:46 +01:00
@@ -0,0 +1,79 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef BLOCK_MUTEX_IMPL_HPP
+#define BLOCK_MUTEX_IMPL_HPP
+
+#include "ArrayPool.hpp"
+#include "DLFifoList.hpp"
+#include "KeyTable.hpp"
+#include <signaldata/UtilLock.hpp>
+
+class LockQueue
+{
+public:
+ LockQueue() {}
+
+ /**
+ * A lock queue element
+ */
+ struct LockQueueElement
+ {
+ UtilLockReq m_req;
+ union {
+ Uint32 nextPool;
+ Uint32 nextList;
+ };
+ Uint32 prevList;
+ };
+
+ typedef class ArrayPool<LockQueueElement> Pool;
+
+ Uint32 lock(Pool&, const UtilLockReq * req, const UtilLockReq** lockOwner= 0);
+ Uint32 unlock(Pool&, const UtilUnlockReq* req);
+
+ /**
+ * After unlock
+ */
+ struct Iterator
+ {
+ Pool * thePool;
+ Ptr<LockQueueElement> m_prev;
+ Ptr<LockQueueElement> m_curr;
+ };
+
+ bool first(Pool& pool, Iterator&);
+ bool next(Iterator&);
+
+ /**
+ * 0 - done
+ * 1 - already granted
+ * 2 - needs conf
+ */
+ int checkLockGrant(Iterator &, UtilLockReq * req);
+
+ /**
+ * Clear lock queue
+ */
+ void clear (Pool&);
+
+private:
+ /**
+ * The actual lock queue
+ */
+ DLFifoList<LockQueueElement>::Head m_queue;
+};
+
+#endif
diff -Nrup a/storage/ndb/src/kernel/vm/Makefile.am b/storage/ndb/src/kernel/vm/Makefile.am
--- a/storage/ndb/src/kernel/vm/Makefile.am 2006-12-31 01:06:42 +01:00
+++ b/storage/ndb/src/kernel/vm/Makefile.am 2007-11-12 17:13:46 +01:00
@@ -36,7 +36,7 @@ libkernel_a_SOURCES = \
Rope.cpp \
ndbd_malloc.cpp ndbd_malloc_impl.cpp \
Pool.cpp WOPool.cpp RWPool.cpp \
- DynArr256.cpp
+ DynArr256.cpp LockQueue.cpp
INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/mgmapi
diff -Nrup a/storage/ndb/src/kernel/vm/Mutex.cpp b/storage/ndb/src/kernel/vm/Mutex.cpp
--- a/storage/ndb/src/kernel/vm/Mutex.cpp 2006-12-23 20:20:19 +01:00
+++ b/storage/ndb/src/kernel/vm/Mutex.cpp 2007-11-12 17:13:46 +01:00
@@ -113,7 +113,6 @@ SimulatedBlock::MutexManager::destroy(Si
req->senderData = ptr.i;
req->senderRef = m_block.reference();
req->lockId = ptr.p->m_mutexId;
- req->lockKey = ptr.p->m_mutexKey;
m_block.sendSignal(DBUTIL_REF,
GSN_UTIL_DESTROY_LOCK_REQ,
@@ -150,32 +149,16 @@ SimulatedBlock::MutexManager::execUTIL_D
void
-SimulatedBlock::MutexManager::lock(Signal* signal, ActiveMutexPtr& ptr){
+SimulatedBlock::MutexManager::lock(Signal* signal,
+ ActiveMutexPtr& ptr,
+ Uint32 flags){
UtilLockReq * req = (UtilLockReq*)signal->getDataPtrSend();
req->senderData = ptr.i;
req->senderRef = m_block.reference();
req->lockId = ptr.p->m_mutexId;
- req->requestInfo = 0;
-
- m_block.sendSignal(DBUTIL_REF,
- GSN_UTIL_LOCK_REQ,
- signal,
- UtilLockReq::SignalLength,
- JBB);
-
- ptr.p->m_gsn = GSN_UTIL_LOCK_REQ;
-}
-
-void
-SimulatedBlock::MutexManager::trylock(Signal* signal, ActiveMutexPtr& ptr){
+ req->requestInfo = flags;
- UtilLockReq * req = (UtilLockReq*)signal->getDataPtrSend();
- req->senderData = ptr.i;
- req->senderRef = m_block.reference();
- req->lockId = ptr.p->m_mutexId;
- req->requestInfo = UtilLockReq::TryLock;
-
m_block.sendSignal(DBUTIL_REF,
GSN_UTIL_LOCK_REQ,
signal,
@@ -193,8 +176,20 @@ SimulatedBlock::MutexManager::execUTIL_L
ndbrequire(ptr.p->m_gsn == GSN_UTIL_LOCK_REQ);
ndbrequire(ptr.p->m_mutexId == ref->lockId);
- ptr.p->m_gsn = 0;
+ bool notify = ref->errorCode == UtilLockRef::InLockQueue;
+ CallbackFunction fun = ptr.p->m_callback.m_callbackFunction;
+
+ if (!notify)
+ {
+ ptr.p->m_gsn = 0;
+ }
m_block.execute(signal, ptr.p->m_callback, ref->errorCode);
+
+ if (notify)
+ {
+ // execute clears function so that same callback shouldnt be called twice
+ ptr.p->m_callback.m_callbackFunction = fun;
+ }
}
void
@@ -204,9 +199,7 @@ SimulatedBlock::MutexManager::execUTIL_L
m_activeMutexes.getPtr(ptr, conf->senderData);
ndbrequire(ptr.p->m_gsn == GSN_UTIL_LOCK_REQ);
ndbrequire(ptr.p->m_mutexId == conf->lockId);
-
- ptr.p->m_mutexKey = conf->lockKey;
-
+
ptr.p->m_gsn = 0;
m_block.execute(signal, ptr.p->m_callback, 0);
}
@@ -217,7 +210,6 @@ SimulatedBlock::MutexManager::unlock(Sig
req->senderData = ptr.i;
req->senderRef = m_block.reference();
req->lockId = ptr.p->m_mutexId;
- req->lockKey = ptr.p->m_mutexKey;
m_block.sendSignal(DBUTIL_REF,
GSN_UTIL_UNLOCK_REQ,
diff -Nrup a/storage/ndb/src/kernel/vm/Mutex.hpp b/storage/ndb/src/kernel/vm/Mutex.hpp
--- a/storage/ndb/src/kernel/vm/Mutex.hpp 2006-12-23 20:20:19 +01:00
+++ b/storage/ndb/src/kernel/vm/Mutex.hpp 2007-11-12 17:13:46 +01:00
@@ -18,6 +18,7 @@
#include "Callback.hpp"
#include "SimulatedBlock.hpp"
+#include <signaldata/UtilLock.hpp>
class Mutex;
@@ -69,11 +70,11 @@ public:
void release();
bool isNull() const ;
- bool lock(SimulatedBlock::Callback & callback);
- bool trylock(SimulatedBlock::Callback & callback);
+ bool lock(SimulatedBlock::Callback & callback, bool exclusive = true, bool notify =
false);
+ bool trylock(SimulatedBlock::Callback & callback, bool exclusive = true);
void unlock(SimulatedBlock::Callback & callback);
void unlock(); // Ignore callback
-
+
bool create(SimulatedBlock::Callback & callback);
bool destroy(SimulatedBlock::Callback & callback);
@@ -181,12 +182,14 @@ Mutex::isNull() const {
inline
bool
-Mutex::lock(SimulatedBlock::Callback & callback){
+Mutex::lock(SimulatedBlock::Callback & callback, bool exclusive, bool notify){
if(m_ptr.isNull()){
if(m_mgr.seize(m_ptr)){
m_ptr.p->m_mutexId = m_mutexId;
m_ptr.p->m_callback = callback;
- m_mgr.lock(m_signal, m_ptr);
+ m_mgr.lock(m_signal, m_ptr,
+ ((exclusive == false) ? UtilLockReq::SharedLock : 0) |
+ ((notify == true) ? UtilLockReq::Notify : 0));
return true;
}
return false;
@@ -198,12 +201,14 @@ Mutex::lock(SimulatedBlock::Callback & c
inline
bool
-Mutex::trylock(SimulatedBlock::Callback & callback){
+Mutex::trylock(SimulatedBlock::Callback & callback, bool exclusive){
if(m_ptr.isNull()){
if(m_mgr.seize(m_ptr)){
m_ptr.p->m_mutexId = m_mutexId;
m_ptr.p->m_callback = callback;
- m_mgr.lock(m_signal, m_ptr);
+ m_mgr.lock(m_signal, m_ptr,
+ UtilLockReq::TryLock |
+ ((exclusive == false) ? UtilLockReq::SharedLock : 0));
return true;
}
return false;
diff -Nrup a/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp
--- a/storage/ndb/src/kernel/vm/SimulatedBlock.cpp 2007-06-14 16:10:10 +02:00
+++ b/storage/ndb/src/kernel/vm/SimulatedBlock.cpp 2007-11-12 17:13:46 +01:00
@@ -96,7 +96,8 @@ SimulatedBlock::SimulatedBlock(BlockNumb
count = 5;
BaseString::snprintf(buf, 255, "%s.ActiveMutexes", getBlockName(blockNumber));
if(!p->get(buf, &count))
- p->get("ActiveMutexes", &count);
+ if (!this->getParam("ActiveMutexes", &count))
+ p->get("ActiveMutexes", &count);
c_mutexMgr.setSize(count);
c_counterMgr.setSize(5);
diff -Nrup a/storage/ndb/src/kernel/vm/SimulatedBlock.hpp
b/storage/ndb/src/kernel/vm/SimulatedBlock.hpp
--- a/storage/ndb/src/kernel/vm/SimulatedBlock.hpp 2007-07-11 14:38:00 +02:00
+++ b/storage/ndb/src/kernel/vm/SimulatedBlock.hpp 2007-11-12 17:13:46 +01:00
@@ -480,7 +480,6 @@ public:
struct ActiveMutex {
Uint32 m_gsn; // state
Uint32 m_mutexId;
- Uint32 m_mutexKey;
Callback m_callback;
union {
Uint32 nextPool;
@@ -497,8 +496,7 @@ public:
void create(Signal*, ActiveMutexPtr&);
void destroy(Signal*, ActiveMutexPtr&);
- void lock(Signal*, ActiveMutexPtr&);
- void trylock(Signal*, ActiveMutexPtr&);
+ void lock(Signal*, ActiveMutexPtr&, Uint32 flags);
void unlock(Signal*, ActiveMutexPtr&);
private:
@@ -523,6 +521,7 @@ public:
MutexManager c_mutexMgr;
void ignoreMutexUnlockCallback(Signal* signal, Uint32 ptrI, Uint32 retVal);
+ virtual bool getParam(const char * param, Uint32 * retVal) { return false;}
SafeCounterManager c_counterMgr;
private:
| Thread |
|---|
| • bk commit into 5.1 tree (jonas:1.2675) | jonas | 12 Nov |