From 666a951c5f77d919c9cbd08b8bf9c68412825824 Mon Sep 17 00:00:00 2001 From: Dmitry Barashev Date: Sat, 28 Sep 2024 16:27:52 +0400 Subject: [PATCH] Corrected two-phase barrier behavior --- .../java/biz/ganttproject/app/Barriers.kt | 22 +++++--- .../ganttproject/GanttProjectImpl.kt | 1 + .../java/biz/ganttproject/app/BarriersTest.kt | 53 +++++++++++++++++++ 3 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 ganttproject/src/test/java/biz/ganttproject/app/BarriersTest.kt diff --git a/ganttproject/src/main/java/biz/ganttproject/app/Barriers.kt b/ganttproject/src/main/java/biz/ganttproject/app/Barriers.kt index 8107d6ec54..ff8b4f5212 100644 --- a/ganttproject/src/main/java/biz/ganttproject/app/Barriers.kt +++ b/ganttproject/src/main/java/biz/ganttproject/app/Barriers.kt @@ -75,8 +75,17 @@ class TwoPhaseBarrierImpl(private val value: T) : Barrier, BarrierEntrance private val counter = AtomicInteger(0) private val exits = mutableListOf>() private val activities = mutableMapOf() + + var isActive: Boolean = false + set(value) { + field = value + if (value && counter.get() == 0) { + exits.forEach { it(this.value) } + } + } + override fun await(code: BarrierExit) { - if (counter.get() == 0) { + if (isActive && counter.get() == 0) { code(value) } else { exits.add(code) @@ -84,12 +93,16 @@ class TwoPhaseBarrierImpl(private val value: T) : Barrier, BarrierEntrance } override fun register(activity: String): OnBarrierReached { + assert(!isActive) { + "You need to register barrier activities before it gets active" + } return { if (counter.get() > 0) { BARRIER_LOGGER.debug("Barrier reached: $activity") //println("Barrier reached: $activity") activities.remove(activity) - tick() + counter.decrementAndGet() + isActive = true } }.also { BARRIER_LOGGER.debug("Barrier waiting: $activity") @@ -98,11 +111,6 @@ class TwoPhaseBarrierImpl(private val value: T) : Barrier, BarrierEntrance counter.incrementAndGet() } } - private fun tick() { - if (counter.decrementAndGet() == 0) { - exits.forEach { it(value) } - } - } } /** diff --git a/ganttproject/src/main/java/net/sourceforge/ganttproject/GanttProjectImpl.kt b/ganttproject/src/main/java/net/sourceforge/ganttproject/GanttProjectImpl.kt index d6112b5e80..4be5773191 100644 --- a/ganttproject/src/main/java/net/sourceforge/ganttproject/GanttProjectImpl.kt +++ b/ganttproject/src/main/java/net/sourceforge/ganttproject/GanttProjectImpl.kt @@ -149,6 +149,7 @@ open class GanttProjectImpl( for (l in listeners) { l.projectOpened(barrier, barrier) } + barrier.isActive = true } @Throws(Document.DocumentException::class, IOException::class) diff --git a/ganttproject/src/test/java/biz/ganttproject/app/BarriersTest.kt b/ganttproject/src/test/java/biz/ganttproject/app/BarriersTest.kt new file mode 100644 index 0000000000..5cf8834501 --- /dev/null +++ b/ganttproject/src/test/java/biz/ganttproject/app/BarriersTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2024 BarD Software s.r.o., Dmitry Barashev. + * + * This file is part of GanttProject, an opensource project management tool. + * + * GanttProject 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, either version 3 of the License, or + * (at your option) any later version. + * + * GanttProject 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 GanttProject. If not, see . + */ +package biz.ganttproject.app + +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class BarriersTest { + + @Test + fun `two phase barrier exit registered before entrance`() { + var exitCalled = false + val barrier = TwoPhaseBarrierImpl(true) + barrier.await { + assertTrue(it) + exitCalled = true + } + assertFalse(exitCalled) + + barrier.register("Entrance activity").let { entrance -> entrance() } + assertTrue(exitCalled) + } + + @Test + fun `two phase barrier exit registered after entrance`() { + var exitCalled = false + val barrier = TwoPhaseBarrierImpl(true) + barrier.register("Entrance activity").let { entrance -> entrance() } + barrier.await { + assertTrue(it) + exitCalled = true + } + assertTrue(exitCalled) + } + +} \ No newline at end of file