⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 transaction.java

📁 Java的面向对象数据库系统的源代码
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
        // Thread.currentThread().yield();

        // transaktion als blockiert markieren (kante in lock-graphen einfuegen);
        // vor deadlock-erkennung: es werden auch deadlocks mit
        // transaktionen erkannt, die selber erstmal auf deadlock checken
        // aber auf keinem fall zuwenige (race-cond. zwischen deadlock-pruefung
        // und lock setzen)
        blocker = container.id();
        blockTimeout = System.currentTimeMillis() + 100;

        // this may happen when the container was deactivated between
        // acquireObject() and this point; I'm not sure what to do here

        // But I (Xu鈔 Baldauf, Medium.net) am (partly):
        // If our caller is acquireContainer(), the container is pinned and thus may not be deactived inbetween.
        // If our caller is createObject(), the container is pinned and thus may not be deactived inbetween.
        if (container.lock() == null) {
            throw new IllegalStateException("Container was wrongly deactivated. Increasing heap memory of the JVM may help.");
        }

        // try to aquire the lock of the container; wait until the locks is
        // successfully aquired
        int prevLevel = container.lock().tryAcquire(this, lockLevel);


        // TODO: FIXME: HACK: prevents strange seldom occuring hang; somewhere
        // it is possible that a tx ends, but is still listed as locking a
        // cluster. This tx would then wait() to be notified by that other tx
        // once it gives up its lock on that container, but since that other tx
        // has already stopped there is no way this tx will ever be notify()ed.
        // This hack is repairing the damage done by unlocking a cluster locked
        // by 'ghost' tx-es. We should find out where the real cause is...
        if (prevLevel == Lock.NOT_ACQUIRED) {
            DxIterator i = container.lock().lockerIDs().iterator();
            TransactionID txID;
            while ((txID = (TransactionID) i.next()) != null) {
                Transaction tx = env.transactionManager.taForID(txID);
                if (tx == null) {
                    env.logWriter.newEntry(this, " lock " + container.lock() + " locked by txID " 
                            + txID + ", but that tx does not exist anymore; compensating... Please report this error to the ozone developers list", LogWriter.ERROR);
                    
                    // locks only use the transactions id, so this should work
                    Transaction hack = new FixHack(txID);
                    container.lock().release(hack);
                }
            }
            prevLevel = container.lock().tryAcquire(this, lockLevel);
        }
        // end of HACK,


        if (prevLevel == Lock.NOT_ACQUIRED) {
            synchronized (this) {
                while (prevLevel == Lock.NOT_ACQUIRED) {
                    try {
                        if (env.logWriter.hasTarget(LogWriter.DEBUG2)) {
                            env.logWriter.newEntry(this, toString() + " blocked by lock " + container.lock() + "...", LogWriter.DEBUG2);
                        }

                        wait();

                        if (env.logWriter.hasTarget(LogWriter.DEBUG2)) {
                            env.logWriter.newEntry(this, toString() + " checking lock again...", LogWriter.DEBUG2);
                        }
                    } catch (Exception e) {
                        // do nothing; just proceed...
                    }

                    /*
                        We have two cases:
                        (1) We are called by acquireObject()
                        (2) We are not called by acquireObject()

                        In case (1), we do not need to reload, because the object is pinned.
                        In case (2), we never come to this code location, because in this case, the objects are freshly created and thus never locked.

                        Thus, we never need to reload.

                        Oh, the reasoning above does not work out. If a cluster is pinned,
                        it still may be aborted and thus need to reload. But maybe then the
                        "pinned" concept is mood. Maybe pinned clusters which are arborted
                        should be reloaded immediately.
                    */

                    // since the container was maybe deactivated while waiting we
                    // reload it here again
                    ObjectContainer newContainer = env.storeManager.containerForID(this, blocker);

                    if (container == null) {
                        throw new ObjectNotFoundException("No such object.");
                    }

                    if (container != newContainer ) {
                        // the container has been deactivated
                        if (container.isPinned()) {
                            // pinCount moving
                            while (container.isPinned()) {
                                newContainer.pin();
                                container.unpin();
                            }
                            // remove additional pin which is set during container reloading 
                            newContainer.unpin();
                            // remove additional pin which is set during loadCluster() just after unloadCluster()
                            // see: ClusterStore.abortCluster();
                            newContainer.unpin();
                        }
                        container = newContainer;
                    }
                    // remove additional pin which was set during container reloading
                    // it occurs while container reloading if the "newContainer" is a same as the "container"
                    container.unpin();

                    // throw an exception if we are forced to abort because of a deadlock
                    container.lock().checkDeadlock(this);

                    prevLevel = container.lock().tryAcquire(this, lockLevel);
                }
            }
        }

        if (env.logWriter.hasTarget(LogWriter.DEBUG1)) {
            env.logWriter.newEntry(this, toString() + ".acquireContainer(" + blocker + "): successful.", LogWriter.DEBUG1);
        }

        // transaction is no longer blocked
        blocker = null;

        // after acquiring the lock we update the lock level of the container
        if (prevLevel < lockLevel) {
            if (owner == null) {
                throw new PermissionError("No such user.");
            }
            if (!env.userManager.checkPermission(owner, container, lockLevel)) {
                throw new PermissionError("User does not have proper access rights.");
            }

            env.storeManager.updateLockLevel(this, container);
        }
        container.touch();

        return container;
    }

    /**
        A user calls {@link OzoneObject#requireWriteLocking}.

        This means that no user-visible declaed exceptions can be thrown. Thus,
        the same measures regarding catching and wrapping exceptions have to be taken as in {@link #invokeObject}.
    */
    public void requireWriteLockingCallByUser(ObjectContainer container) {
        try {
            ObjectContainer result = acquireContainer(container,Lock.LEVEL_WRITE);

            /*
                Note that we ignore the result from acquireContainer.
                But this should be safe, as acquireContainer should not be able to reload our container,
                because our container is the container which calls us. If acquireContainer did reload,
                we would be in trouble, and this would be a bug.
            */

            assert result==container;
        } catch (OzoneObjectException e) {
            throw e;
        } catch (OzoneRemoteException e) {
            env.logWriter.newEntry(this, "invokeObject()", e, LogWriter.WARN);
            throw e;
        } catch (Exception e) {
            // since we throw away stack trace of the original exception we
            // create a new log message here
            env.logWriter.newEntry(this, "invokeObject()", e, LogWriter.WARN);
            throw new OzoneInternalException(e.toString(), e);
        }
    }


    public boolean performCommand(DbCommand command) {
        if (env.logWriter.hasTarget(LogWriter.DEBUG)) {
            env.logWriter.newEntry(this, "performCommand(): " + toString() + ", " + command.toString(), LogWriter.DEBUG);
        }

        commandCount++;

        boolean result = true;
        try {
            // result goes in command.result
            command.perform(this);
        } catch (TransactionError e) {
            throw e;
        } catch (Throwable e) {
            Throwable resultingException = e;

            if (e instanceof OzoneObjectException) {
                // User exceptions do not need to be verbose. It's the responsibility of the user to print or not to print out the stack trace.
                Throwable cause = e.getCause();

                env.logWriter.newEntry(this, toString() + ": exception in ozone object: (" + e + "): ", e, LogWriter.DEBUG1);
                env.logWriter.newEntry(this, toString() + ": cause:", cause, LogWriter.DEBUG1);

//                  resultingException = cause;
            } else {
                env.logWriter.newEntry(this, toString() + ": uncaught exception: (" + e + ")", e, LogWriter.WARN);
            }

            if (e instanceof PermissionError) {
                e = new PermissionDeniedException(e.toString());
            }

            rollbackOnly = true;
            command.result = resultingException;
            result = false;
        }
        return result;
    }


    public synchronized void prepareCommit() throws IOException, ClassNotFoundException {
        if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
            env.logWriter.newEntry(this, "==> PREPARECOMMIT() ", LogWriter.DEBUG3);
        }
        status = STATUS_PREPARING;
        env.storeManager.prepareCommitTransaction(this);
        status = STATUS_PREPARED;
    }


    /**
     * Commit this transaction. The transaction has to be in PREPARED state.
     * Ones this method is called it MUST commit the entire transaction
     * stuff without any exception.
     */
    public synchronized void commit() throws IOException, ClassNotFoundException {
        if (env.logWriter.hasTarget(LogWriter.DEBUG1)) {
            env.logWriter.newEntry(this, "==> COMMIT() ", LogWriter.DEBUG1);
        }

        status = STATUS_COMMITING;
        try {
            env.storeManager.commitTransaction(this);
        } finally {
            blocker = null;
        }
        status = STATUS_COMMITED;
    }

    /**
     * Once this method is called it MUST cleanup the entire transaction
     * stuff without exception. An exception signals an internal server error.
     * <p>
     * Note: This may be called after/from prepareCommit() !
     */
    public synchronized void abort(DbCommand command) throws IOException, ClassNotFoundException {
        if (env.logWriter.hasTarget(LogWriter.DEBUG1)) {
            env.logWriter.newEntry(this, "==> ABORT() ", LogWriter.DEBUG1);
        }

        status = STATUS_ABORTING;
        try {
            env.storeManager.abortTransaction(this);
        } finally {
            // FIXME: We better do not delete the data now. Somewhere it is used again. (Maybe in case of deadlock retry?)
            blocker = null;
        }
        status = STATUS_ABORTED;
    }


    /**
     * Helper method to implement the Locker interface to support deadlock
     * recognition via core.dr package
     */
    public Lockable blockedBy() {
        try {
            return blocker != null ? (Lockable) env.storeManager.containerForID(this, blocker) : null;
        } catch (NullPointerException e) {
            env.logWriter.newEntry(this, "blockedBy(): Our blocker is invalidated. We are not blocked anymore?", e, LogWriter.ERROR);

            blocker = null;

            return null;
        } catch (Exception e) {
            env.logWriter.newEntry(this, "blockedBy() ", e, LogWriter.ERROR);
            throw new RuntimeException("blockedBy() FAILED!");
        }
    }

    /**
     Returns wether this locker is blocked.
     */
    public boolean isBlocked() {
        return (blocker != null && System.currentTimeMillis() > blockTimeout);
    }

    /**
     * Create a new database object. If the className is null, an empty container
     * is created.
     *
     * @param className
     * @param access
     * @param name
     * @param id The ID of the new container or null.
     * @exception PermissionDeniedException If user in invalid, name is already in
     * use, target is not OzoneCompatible...
     */
    public ObjectContainer createObject(String className, int access, String name, String sig, Object[] args, ObjectID id) throws Exception, org.ozoneDB.OzoneObjectException {
        if (env.logWriter.hasTarget(LogWriter.DEBUG3)) {
            env.logWriter.newEntry(this, "createObject() ", LogWriter.DEBUG3);
        }

        // check (and wait) if a thread runs exclusively;
        env.transactionManager.checkExclusion();

        try {
            Class cl = null;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -