diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java
index 4ab425c4527..e35e8cb1ad4 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/DBTest.java
@@ -249,7 +249,7 @@ public class DBTest extends BaseTestCase {
IString biss = db.newString(b);
IString aisc = db.newString(acs);
IString bisc = db.newString(bcs);
- db.setReadOnly();
+ db.setReadOnly(true);
assertSignEquals(expected, aiss.compare(bcs, caseSensitive));
assertSignEquals(expected, aiss.compare(biss, caseSensitive));
diff --git a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMBugsTest.java b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMBugsTest.java
index b07a836d4db..e3ec589fd7d 100644
--- a/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMBugsTest.java
+++ b/core/org.eclipse.cdt.core.tests/parser/org/eclipse/cdt/internal/pdom/tests/PDOMBugsTest.java
@@ -66,7 +66,7 @@ public class PDOMBugsTest extends BaseTestCase {
wpdom.setProperty("c", "e");
assertEquals("e", wpdom.getProperty("c"));
} finally {
- pdom.releaseWriteLock(0);
+ pdom.releaseWriteLock(0, true);
}
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndex.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndex.java
index 20d625455ea..ab99788e608 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndex.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndex.java
@@ -67,8 +67,16 @@ public interface IWritableIndex extends IIndex {
/**
* Releases a write lock, reestablishing a certain amount of read locks.
+ * Fully equivalent to releaseWriteLock(int, true)
.
*/
void releaseWriteLock(int establishReadLockCount);
+
+ /**
+ * Releases a write lock, reestablishing a certain amount of read locks.
+ * @param establishReadLockCount amount of read-locks to establish.
+ * @param flushDatabase when true the changes are flushed to disk.
+ */
+ void releaseWriteLock(int establishReadLockCount, boolean flushDatabase);
/**
* Resets the counters for cache-hits
@@ -90,4 +98,9 @@ public interface IWritableIndex extends IIndex {
* no writable fragment.
*/
IWritableIndexFragment getPrimaryWritableFragment();
+
+ /**
+ * Flushes all caches to the disk.
+ */
+ void flush() throws CoreException;
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndexFragment.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndexFragment.java
index a42b339f2d2..3df054c60d1 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndexFragment.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/IWritableIndexFragment.java
@@ -60,8 +60,10 @@ public interface IWritableIndexFragment extends IIndexFragment {
/**
* Releases a write lock, reestablishing a certain amount of read locks.
+ * @param establishReadLockCount amount of read-locks to establish
+ * @param flush if true
changes are flushed to disk
*/
- void releaseWriteLock(int establishReadLockCount);
+ void releaseWriteLock(int establishReadLockCount, boolean flush);
/**
* Write the key, value mapping to the fragment properties. If a mapping for the
@@ -72,4 +74,9 @@ public interface IWritableIndexFragment extends IIndexFragment {
* @throws NullPointerException if key is null
*/
public void setProperty(String propertyName, String value) throws CoreException;
+
+ /**
+ * Flushes caches to disk.
+ */
+ void flush() throws CoreException;
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/WritableCIndex.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/WritableCIndex.java
index 7bf171f3bc0..8e5cc2abc4e 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/WritableCIndex.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/index/WritableCIndex.java
@@ -113,24 +113,37 @@ public class WritableCIndex extends CIndex implements IWritableIndex {
// rollback
fIsWriteLocked= false;
while (--i >= 0) {
- fWritableFragments[i].releaseWriteLock(giveupReadlockCount);
+ fWritableFragments[i].releaseWriteLock(giveupReadlockCount, false);
}
}
}
}
public synchronized void releaseWriteLock(int establishReadlockCount) {
+ releaseWriteLock(establishReadlockCount, true);
+ }
+
+ public synchronized void releaseWriteLock(int establishReadlockCount, boolean flush) {
assert fIsWriteLocked: "No write lock to be released"; //$NON-NLS-1$
assert establishReadlockCount == getReadLockCount(): "Unexpected read lock is not allowed"; //$NON-NLS-1$
fIsWriteLocked= false;
- int i= 0;
- for (i = 0; i < fWritableFragments.length; i++) {
- fWritableFragments[i].releaseWriteLock(establishReadlockCount);
+ for (int i = 0; i < fWritableFragments.length; i++) {
+ fWritableFragments[i].releaseWriteLock(establishReadlockCount, flush);
}
}
public IWritableIndexFragment getPrimaryWritableFragment() {
return fWritableFragments.length > 0 ? fWritableFragments[0] : null;
}
+
+ public void flush() throws CoreException {
+ assert !fIsWriteLocked;
+ int i= 0;
+ for (i = 0; i < fWritableFragments.length; i++) {
+ fWritableFragments[i].flush();
+ }
+ }
+
+
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java
index b8235c594dc..c11676821a9 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOM.java
@@ -564,12 +564,12 @@ public class PDOM extends PlatformObject implements IIndexFragment, IPDOM {
}
final public void releaseWriteLock() {
- releaseWriteLock(0);
+ releaseWriteLock(0, true);
}
-
- public void releaseWriteLock(int establishReadLocks) {
+
+ public void releaseWriteLock(int establishReadLocks, boolean flush) {
try {
- db.setReadOnly();
+ db.setReadOnly(flush);
} catch (CoreException e) {
CCorePlugin.log(e);
}
@@ -736,5 +736,9 @@ public class PDOM extends PlatformObject implements IIndexFragment, IPDOM {
public void resetCacheCounters() {
db.resetCacheCounters();
+ }
+
+ public void flush() throws CoreException {
+ db.flush();
}
}
\ No newline at end of file
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java
index acf5f697f5b..813335b3cae 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMManager.java
@@ -1037,8 +1037,8 @@ public class PDOMManager implements IWritableIndexManager, IListener {
PDOM pdom= (PDOM) getPDOM(cproject);
pdom.acquireWriteLock();
try {
+ pdom.flush();
Database db= pdom.getDB();
- db.flushDirtyChunks();
FileChannel from= db.getChannel();
FileChannel to = new FileOutputStream(targetLocation).getChannel();
from.transferTo(0, from.size(), to);
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java
index 71be85e5ddf..675bfa57db9 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/PDOMWriter.java
@@ -108,6 +108,17 @@ abstract public class PDOMWriter {
*/
protected abstract IIndexFileLocation findLocation(String absolutePath);
+ /**
+ * Fully equivalent to
+ * addSymbols(IASTTranslationUnit, IWritableIndex, int, true, int, IProgressMonitor)
.
+ * @since 4.0
+ */
+ public void addSymbols(IASTTranslationUnit ast,
+ IWritableIndex index, int readlockCount,
+ int configHash, IProgressMonitor pm) throws InterruptedException, CoreException {
+ addSymbols(ast, index, readlockCount, true, configHash, pm);
+ }
+
/**
* Extracts symbols from the given ast and adds them to the index. It will
* make calls to
@@ -115,10 +126,14 @@ abstract public class PDOMWriter {
* {@link #postAddToIndex(IIndexFileLocation, IIndexFile)},
* {@link #getLastModified(IIndexFileLocation)} and
* {@link #findLocation(String)} to obtain further information.
+ *
+ * When flushIndex is set to false
, you must make sure to flush the
+ * index after your last write operation.
* @since 4.0
*/
- public void addSymbols(IASTTranslationUnit ast, IWritableIndex index, int readlockCount, int configHash,
- IProgressMonitor pm) throws InterruptedException, CoreException {
+ public void addSymbols(IASTTranslationUnit ast,
+ IWritableIndex index, int readlockCount, boolean flushIndex,
+ int configHash, IProgressMonitor pm) throws InterruptedException, CoreException {
final Map symbolMap= new HashMap();
try {
HashSet contextIncludes= new HashSet();
@@ -187,7 +202,7 @@ abstract public class PDOMWriter {
}
}
} finally {
- index.releaseWriteLock(readlockCount);
+ index.releaseWriteLock(readlockCount, flushIndex);
}
fStatistics.fAddToIndexTime+= System.currentTimeMillis()-start;
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/TeamPDOMImportOperation.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/TeamPDOMImportOperation.java
index 9f74e3b2194..e238e3499b9 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/TeamPDOMImportOperation.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/TeamPDOMImportOperation.java
@@ -282,7 +282,7 @@ public class TeamPDOMImportOperation implements IWorkspaceRunnable {
}
}
finally {
- pdom.releaseWriteLock(giveupReadlocks);
+ pdom.releaseWriteLock(giveupReadlocks, true);
}
}
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Chunk.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Chunk.java
index 7a368e87549..fd90a1488d1 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Chunk.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Chunk.java
@@ -28,20 +28,24 @@ final class Chunk {
boolean fCacheHitFlag= false;
boolean fDirty= false;
- boolean fWritable= false;
+ boolean fLocked= false; // locked chunks must not be released from cache.
int fCacheIndex= -1;
- Chunk(Database db, int sequenceNumber) throws CoreException {
+ Chunk(Database db, int sequenceNumber) {
fDatabase= db;
fBuffer= ByteBuffer.allocate(Database.CHUNK_SIZE);
fSequenceNumber= sequenceNumber;
+ }
+
+ void read() throws CoreException {
try {
+ fBuffer.position(0);
fDatabase.getChannel().read(fBuffer, fSequenceNumber*Database.CHUNK_SIZE);
} catch (IOException e) {
throw new CoreException(new DBStatus(e));
}
}
-
+
void flush() throws CoreException {
try {
fBuffer.position(0);
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ChunkCache.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ChunkCache.java
index 36be4aa0f29..84dded98fe3 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ChunkCache.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/ChunkCache.java
@@ -30,9 +30,9 @@ public final class ChunkCache {
fPageTable= new Chunk[computeLength(maxSize)];
}
- public synchronized void add(Chunk chunk, boolean writable) {
- if (writable) {
- chunk.fWritable= true;
+ public synchronized void add(Chunk chunk, boolean locked) {
+ if (locked) {
+ chunk.fLocked= true;
}
if (chunk.fCacheIndex >= 0) {
chunk.fCacheHitFlag= true;
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
index 1e93f32588e..e3a3ea27d09 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/db/Database.java
@@ -95,7 +95,7 @@ public class Database {
setWritable();
createNewChunk();
setVersion(version);
- setReadOnly();
+ setReadOnly(true);
}
} catch (IOException e) {
throw new CoreException(new DBStatus(e));
@@ -158,24 +158,25 @@ public class Database {
// for performance reasons try to find chunk and mark it without
// synchronizing. This means that we might pick up a chunk that
- // has been paged out, which is ok.
- // Furthermore the hitflag may not be seen by the clock-alorithm,
+ // has been paged out, which is fine.
+ // Furthermore the hit-flag may not be seen by the clock-algorithm,
// which might lead to the eviction of a chunk. With the next
// cache failure we are in sync again, though.
Chunk chunk = chunks[index];
- if (chunk != null && chunk.fWritable == fWritable) {
+ if (chunk != null && (chunk.fLocked || !fWritable)) {
chunk.fCacheHitFlag= true;
cacheHits++;
return chunk;
}
// here is the safe code that has to be performed if we cannot
- // get ahold of the chunk.
+ // get hold of the chunk.
synchronized(fCache) {
chunk= chunks[index];
if (chunk == null) {
cacheMisses++;
chunk = chunks[index] = new Chunk(this, index);
+ chunk.read();
}
else {
cacheHits++;
@@ -239,18 +240,16 @@ public class Database {
}
private int createNewChunk() throws CoreException {
- try {
- Chunk[] oldtoc = chunks;
- int n = oldtoc.length;
- int offset = n * CHUNK_SIZE;
- file.seek(offset);
- file.write(new byte[CHUNK_SIZE]);
- chunks = new Chunk[n + 1];
- System.arraycopy(oldtoc, 0, chunks, 0, n);
- return offset;
- } catch (IOException e) {
- throw new CoreException(new DBStatus(e));
- }
+ Chunk[] oldtoc = chunks;
+ int n = oldtoc.length;
+ int offset = n * CHUNK_SIZE;
+ chunks = new Chunk[n + 1];
+ System.arraycopy(oldtoc, 0, chunks, 0, n);
+ final Chunk chunk= new Chunk(this, n);
+ chunk.fDirty= true;
+ chunks[n]= chunk;
+ fCache.add(chunk, true);
+ return offset;
}
private int getFirstBlock(int blocksize) throws CoreException {
@@ -390,12 +389,12 @@ public class Database {
/**
* Closes the database.
*
- * The behaviour of any further calls to the Database is undefined + * The behavior of any further calls to the Database is undefined * @throws IOException * @throws CoreException */ public void close() throws CoreException { - setReadOnly(); + setReadOnly(true); removeChunksFromCache(); chunks= new Chunk[0]; try { @@ -415,9 +414,10 @@ public class Database { /** * Called from any thread via the cache, protected by {@link #fCache}. */ - void releaseChunk(Chunk chunk) { - if (!chunk.fWritable) + void releaseChunk(final Chunk chunk) { + if (!chunk.fLocked) { chunks[chunk.fSequenceNumber]= null; + } } /** @@ -432,38 +432,88 @@ public class Database { fWritable= true; } - public void setReadOnly() throws CoreException { + public void setReadOnly(final boolean flush) throws CoreException { if (fWritable) { fWritable= false; - flushDirtyChunks(); - } - } - - public void flushDirtyChunks() throws CoreException { - ArrayList dirtyChunks= new ArrayList(); - synchronized (fCache) { - for (int i = 0; i < chunks.length; i++) { - Chunk chunk= chunks[i]; - if (chunk != null && chunk.fWritable) { - chunk.fWritable= false; - if (chunk.fCacheIndex < 0) { - chunks[i]= null; - } - if (chunk.fDirty) { - dirtyChunks.add(chunk); + + ArrayList dirtyChunks= new ArrayList(); + synchronized (fCache) { + for (int i= chunks.length-1; i >= 0 ; i--) { + Chunk chunk= chunks[i]; + if (chunk != null) { + if (chunk.fCacheIndex < 0) { + chunk.fLocked= false; + chunks[i]= null; + if (chunk.fDirty) { + dirtyChunks.add(chunk); + } + } + else if (chunk.fLocked) { + if (!chunk.fDirty) { + chunk.fLocked= false; + } + else if (flush) { + chunk.fLocked= false; + dirtyChunks.add(chunk); + } + } + else if (flush && chunk.fDirty) { + dirtyChunks.add(chunk); + } } } } + + if (!dirtyChunks.isEmpty()) { + for (Iterator it = dirtyChunks.iterator(); it.hasNext();) { + Chunk chunk = (Chunk) it.next(); + chunk.flush(); + } + } } - + } + + public void flush() throws CoreException { + if (fWritable) { + try { + setReadOnly(true); + } + finally { + setWritable(); + } + return; + } + + // be careful as other readers may access chunks concurrently + ArrayList dirtyChunks= new ArrayList(); + synchronized (fCache) { + for (int i= chunks.length-1; i >= 0 ; i--) { + Chunk chunk= chunks[i]; + if (chunk != null && chunk.fDirty) { + dirtyChunks.add(chunk); + } + } + } + if (!dirtyChunks.isEmpty()) { for (Iterator it = dirtyChunks.iterator(); it.hasNext();) { Chunk chunk = (Chunk) it.next(); chunk.flush(); } } + + // only after the chunks are flushed we may unlock and release them. + synchronized (fCache) { + for (Iterator it = dirtyChunks.iterator(); it.hasNext();) { + Chunk chunk = (Chunk) it.next(); + chunk.fLocked= false; + if (chunk.fCacheIndex < 0) { + chunks[chunk.fSequenceNumber]= null; + } + } + } } - + public void resetCacheCounters() { cacheHits= cacheMisses= 0; } diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/export/GeneratePDOM.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/export/GeneratePDOM.java index 3198778ff4e..f7f94de0a14 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/export/GeneratePDOM.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/export/GeneratePDOM.java @@ -69,18 +69,22 @@ public class GeneratePDOM implements ISafeRunnable { try { CCoreInternals.getPDOMManager().exportProjectPDOM(cproject, targetLocation, converter); WritablePDOM exportedPDOM= new WritablePDOM(targetLocation, converter, LanguageManager.getInstance().getPDOMLinkageFactoryMappings()); - - exportedPDOM.acquireWriteLock(0); try { - Map exportProperties= pm.getExportProperties(); - if(exportProperties!=null) { - for(Iterator i = exportProperties.entrySet().iterator(); i.hasNext(); ) { - Map.Entry entry = (Map.Entry) i.next(); - exportedPDOM.setProperty((String) entry.getKey(), (String) entry.getValue()); + exportedPDOM.acquireWriteLock(0); + try { + Map exportProperties= pm.getExportProperties(); + if(exportProperties!=null) { + for(Iterator i = exportProperties.entrySet().iterator(); i.hasNext(); ) { + Map.Entry entry = (Map.Entry) i.next(); + exportedPDOM.setProperty((String) entry.getKey(), (String) entry.getValue()); + } } + } finally { + exportedPDOM.releaseWriteLock(0, true); } - } finally { - exportedPDOM.releaseWriteLock(0); + } + finally { + exportedPDOM.close(); } } catch(InterruptedException ie) { String msg= MessageFormat.format(Messages.GeneratePDOM_GenericGenerationFailed, new Object[] {ie.getMessage()}); diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java index 61b63c9b4db..aad65400013 100644 --- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java +++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/pdom/indexer/PDOMIndexerTask.java @@ -176,6 +176,15 @@ public abstract class PDOMIndexerTask extends PDOMWriter implements IPDOMIndexer * @since 4.0 */ protected void parseTUs(IWritableIndex index, int readlockCount, Collection sources, Collection headers, IProgressMonitor monitor) throws CoreException, InterruptedException { + try { + internalParseTUs(index, readlockCount, sources, headers, monitor); + } + finally { + index.flush(); + } + } + + private void internalParseTUs(IWritableIndex index, int readlockCount, Collection sources, Collection headers, IProgressMonitor monitor) throws CoreException, InterruptedException { int options= 0; if (checkProperty(IndexerPreferences.KEY_SKIP_ALL_REFERENCES)) { options |= AbstractLanguage.OPTION_SKIP_FUNCTION_BODIES; @@ -273,7 +282,7 @@ public abstract class PDOMIndexerTask extends PDOMWriter implements IPDOMIndexer IASTTranslationUnit ast= createAST(tu, scanner, options, pm); fStatistics.fParsingTime += System.currentTimeMillis()-start; if (ast != null) { - addSymbols(ast, index, readlockCount, configHash, pm); + addSymbols(ast, index, readlockCount, false, configHash, pm); } } } @@ -330,7 +339,7 @@ public abstract class PDOMIndexerTask extends PDOMWriter implements IPDOMIndexer fStatistics.fParsingTime += System.currentTimeMillis()-start; if (ast != null) { - addSymbols(ast, index, readlockCount, 0, pm); + addSymbols(ast, index, readlockCount, false, 0, pm); updateInfo(-1, +1, 0); } }