Monitor.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Исходный код среды исполнения ПВТ-ОО.

    Этот исходный код является частью проекта ПВТ-ОО.

    Copyright © 2021 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
*/

package platform.dependent;

public class Monitor(Mutex, Waitable)
{
    private static final int INTERRUPTED = -1;
    private static final int ACTIVE      =  0;
    private static final int WAITING     =  1;
    private static final int SUSPENDED   =  2;


    private int fldEntriesCount;
    private long fldNotifiesCount;
    private MonitorThreadEntry[] fldEntries;
    private MonitorThreadEntry fldOwnedBy;

    public () {  }

    public void lock() {
        boolean needSuspend = false;
        MonitorThreadEntry entry = null;
        super.lock();
        try
        {
            if((entry = fldOwnedBy) == null)
            {
                fldOwnedBy = new MonitorThreadEntry(ACTIVE);
            } else
            {
                long threadId = ThreadEntry.getCurrentThreadId();
                if(entry.threadId == threadId)
                {
                    entry.lockCount++;
                } else
                {
                    needSuspend = true;
                    appendEntry(entry = new MonitorThreadEntry(threadId, SUSPENDED));
                }
            }
        }
        finally
        {
            super.unlock();
        }
        if(needSuspend) entry.waitSignalled(0);
    }

    public void unlock() {
        boolean needThrow = false;
        super.lock();
        try
        {
            MonitorThreadEntry entry = fldOwnedBy;
            if(entry == null || entry.threadId != ThreadEntry.getCurrentThreadId())
            {
                needThrow = true;
            }
            else if(--entry.lockCount == 0L)
            {
                entry.close();
                extractEntry();
            }
        }
        finally
        {
            super.unlock();
        }
        if(needThrow)
        {
            throw new IllegalMonitorStateException("текущий поток исполнения не владеет этим монитором");
        }
    }

    public boolean isHold() { return fldOwnedBy != null; }

    public void interruptio(long threadId) {
        super.lock();
        try
        {
            MonitorThreadEntry entry = getEntryByThreadId(threadId);
            if(entry != null && entry.state == WAITING)
            {
                entry.state = INTERRUPTED;
                if(fldOwnedBy == null) extractEntry();
            }
        }
        finally
        {
            super.unlock();
        }
    }

    public void notify(boolean all, long threadId) {
        boolean needThrow = false;
        super.lock();
        try
        {
            MonitorThreadEntry entry = fldOwnedBy;
            if(entry == null || entry.threadId != ThreadEntry.getCurrentThreadId())
            {
                needThrow = true;
            }
            else if(all)
            {
                for(MonitorThreadEntry[] entries = fldEntries, int i = fldEntriesCount; i-- > 0; )
                {
                    if((entry = entries[i]).state == WAITING) entry.state = SUSPENDED;
                }
            }
            else
            {
                if(threadId == 0L || (entry = getEntryByThreadId(threadId)) == null)
                {
                    entry = getEntryByState(WAITING);
                }
                if(entry != null && entry.state == WAITING)
                {
                    entry.state = SUSPENDED;
                } else
                {
                    fldNotifiesCount++;
                }
            }
        }
        finally
        {
            super.unlock();
        }
        if(needThrow)
        {
            throw new IllegalMonitorStateException("текущий поток исполнения не владеет этим монитором");
        }
    }

    public void wait(int timeInMillis, WaitableHandler handler) throws InterruptedException {
        boolean needThrow = false;
        boolean needSuspend = false;
        boolean needInterrupt = false;
        MonitorThreadEntry entry = null;
        super.lock();
        try
        {
            if((entry = fldOwnedBy) == null || entry.threadId != ThreadEntry.getCurrentThreadId())
            {
                needThrow = true;
            } else
            {
                long notifies = fldNotifiesCount;
                if(notifies != 0L)
                {
                    fldNotifiesCount = notifies - 1L;
                } else
                {
                    needSuspend = true;
                    entry.state = WAITING;
                    extractEntry();
                    appendEntry(entry);
                }
            }
        }
        finally
        {
            super.unlock();
        }
        if(needThrow)
        {
            throw new IllegalMonitorStateException("текущий поток исполнения не владеет этим монитором");
        }
        if(needSuspend)
        {
            if(handler != null) handler.setWaitable(this);
            entry.waitSignalled(timeInMillis);
            do
            {
                super.lock();
                try
                {
                    MonitorThreadEntry ownedby = fldOwnedBy;
                    if(ownedby == null || ownedby == entry)
                    {
                        needSuspend = false;
                        if(ownedby == null)
                        {
                            extractEntry(entry);
                        }
                        if(entry.state == INTERRUPTED)
                        {
                            needInterrupt = true;
                            entry.state = ACTIVE;
                        }
                    }
                    else if(entry.state == WAITING)
                    {
                        entry.state = SUSPENDED;
                    }
                }
                finally
                {
                    super.unlock();
                }
                if(!needSuspend) break;
                entry.waitSignalled(0);
            } while(true);
            if(handler != null) handler.setWaitable(null);
        }
        if(needInterrupt)
        {
            throw new InterruptedException("ожидание было прервано");
        }
    }

    public void wait(long timeInMillis, WaitableHandler handler) throws InterruptedException { Waitable.super.wait(timeInMillis, handler); }

    public void wait(long timeInMillis, int timeInNanos, WaitableHandler handler) throws InterruptedException { Waitable.super.wait(timeInMillis, timeInNanos, handler); }

    private void resume(MonitorThreadEntry entry, boolean needSignal) {
        if((fldOwnedBy = entry).state != INTERRUPTED) entry.state = ACTIVE;
        if(needSignal) entry.setSignalled();
    }

    private void extractEntry() {
        int index = -1;
        int length = fldEntriesCount;
        MonitorThreadEntry[] entries = fldEntries;
        MonitorThreadEntry entry = null;
        label0:
        {
            for(int i = 0; i < length; i++) if((entry = entries[i]).state == INTERRUPTED)
            {
                index = i;
                break label0;
            }
            for(int i = 0; i < length; i++) if((entry = entries[i]).state == SUSPENDED)
            {
                index = i;
                break label0;
            }
            fldOwnedBy = null;
            return;
        }
        Array.copy(entries, index + 1, entries, index, --length - index);
        entries[fldEntriesCount = length] = null;
        resume(entry, true);
    }

    private void extractEntry(MonitorThreadEntry entry) {
        int length = fldEntriesCount;
        MonitorThreadEntry[] entries = fldEntries;
        int index = Array.indexOf(entry, entries, 0, length);
        if(index >= 0)
        {
            Array.copy(entries, index + 1, entries, index, --length - index);
            entries[fldEntriesCount = length] = null;
        }
        resume(entry, false);
    }

    private void appendEntry(MonitorThreadEntry entry) {
        int length = fldEntriesCount;
        MonitorThreadEntry[] entries = fldEntries;
        if(entries == null)
        {
            fldEntries = entries = new MonitorThreadEntry[15];
        }
        else if(length == entries.length)
        {
            Array.copy(entries, 0, fldEntries = entries = new MonitorThreadEntry[(length << 1) + 1], 0, length);
        }
        entries[length++] = entry;
        fldEntriesCount = length;
    }

    private MonitorThreadEntry getEntryByState(int state) {
        for(MonitorThreadEntry[] entries = fldEntries, int length = fldEntriesCount, int i = 0; i < length; i++)
        {
            MonitorThreadEntry entry = entries[i];
            if(entry.state == state) return entry;
        }
        return null;
    }

    private MonitorThreadEntry getEntryByThreadId(long threadId) {
        for(MonitorThreadEntry[] entries = fldEntries, int length = fldEntriesCount, int i = 0; i < length; i++)
        {
            MonitorThreadEntry entry = entries[i];
            if(entry.threadId == threadId) return entry;
        }
        return null;
    }
}