-
-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Uses a graph for ordering steps instead of manual ordering, improved input/output hashing and up-to-date checking. Except for the final outputs which are keyed to the dev bundle, steps are independent of the dev bundle and can be reused between bundles that have the same inputs for the step. For example when using a Paper and Folia 1.21.4 dev bundle in two projects, decompile will only run once as only the final apply dev bundle patches step differs. Final outputs are copied into a project local location to avoid projects sharing a bundle from having a task with a shared output location.
- Loading branch information
Showing
48 changed files
with
2,440 additions
and
1,811 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,7 +45,3 @@ ehthumbs_vista.db | |
!gradle-wrapper.ja | ||
|
||
/.kotlin/ | ||
|
||
# todo remove again | ||
/test/ | ||
/testfork/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file-lock.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/* | ||
* paperweight is a Gradle plugin for the PaperMC project. | ||
* | ||
* Copyright (c) 2023 Kyle Wood (DenWav) | ||
* Contributors | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; | ||
* version 2.1 only, no later versions. | ||
* | ||
* This library 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 | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | ||
* USA | ||
*/ | ||
|
||
package io.papermc.paperweight.util | ||
|
||
import io.papermc.paperweight.PaperweightException | ||
import java.nio.file.Path | ||
import java.util.concurrent.TimeUnit | ||
import java.util.concurrent.locks.ReentrantLock | ||
import kotlin.io.path.* | ||
import org.gradle.api.logging.Logger | ||
import org.gradle.api.logging.Logging | ||
|
||
private val openCurrentJvm: MutableMap<Path, ReentrantLock> = mutableMapOf() | ||
|
||
fun <R> withLock( | ||
lockFile: Path, | ||
printInfoAfter: Long = 1000 * 60 * 5, // 5 minutes | ||
timeoutMs: Long = 1000L * 60 * 60, // one hour | ||
action: () -> R, | ||
): R { | ||
val logger = Logging.getLogger("paperweight lock file") | ||
|
||
var waitedMs = 0L | ||
var firstFailedAcquire = true | ||
while (true) { | ||
val normalized = lockFile.normalize().absolute() | ||
|
||
val lock = synchronized(openCurrentJvm) { | ||
openCurrentJvm.computeIfAbsent(normalized) { ReentrantLock() } | ||
} | ||
if (!lock.tryLock()) { | ||
if (firstFailedAcquire) { | ||
logger.lifecycle("Lock for '$lockFile' is currently held by another thread.") | ||
logger.lifecycle("Waiting for lock to be released...") | ||
firstFailedAcquire = false | ||
} | ||
val startWait = System.nanoTime() | ||
val acquired = lock.tryLock(printInfoAfter, TimeUnit.MILLISECONDS) | ||
if (!acquired) { | ||
waitedMs += (System.nanoTime() - startWait) / 1_000_000 | ||
if (waitedMs >= timeoutMs) { | ||
throw PaperweightException("Have been waiting on lock for '$lockFile' for $waitedMs ms. Giving up as timeout is $timeoutMs ms.") | ||
} | ||
logger.lifecycle( | ||
"Have been waiting on lock for '$lockFile' for ${waitedMs / 1000 / 60} minute(s).\n" + | ||
"If this persists for an unreasonable length of time, kill this process, run './gradlew --stop', and then try again." | ||
) | ||
} | ||
} | ||
val cont = synchronized(openCurrentJvm) { | ||
if (openCurrentJvm[normalized] !== lock) { | ||
lock.unlock() | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
if (cont) { | ||
continue | ||
} | ||
|
||
try { | ||
acquireProcessLockWaiting(lockFile, logger, waitedMs, printInfoAfter, timeoutMs) | ||
try { | ||
return action() | ||
} finally { | ||
lockFile.deleteForcefully() | ||
} | ||
} finally { | ||
synchronized(openCurrentJvm) { | ||
lock.unlock() | ||
openCurrentJvm.remove(normalized) | ||
} | ||
} | ||
} | ||
} | ||
|
||
// TODO: Open an actual exclusive lock using FileChannel | ||
private fun acquireProcessLockWaiting( | ||
lockFile: Path, | ||
logger: Logger, | ||
alreadyWaited: Long = 0, | ||
printInfoAfter: Long, | ||
timeoutMs: Long, | ||
) { | ||
val currentPid = ProcessHandle.current().pid() | ||
|
||
if (lockFile.exists()) { | ||
val lockingProcessId = lockFile.readText().toLong() | ||
if (lockingProcessId == currentPid) { | ||
throw IllegalStateException("Lock file '$lockFile' is currently held by this process.") | ||
} else { | ||
logger.lifecycle("Lock file '$lockFile' is currently held by pid '$lockingProcessId'.") | ||
} | ||
|
||
if (ProcessHandle.of(lockingProcessId).isEmpty) { | ||
logger.lifecycle("Locking process does not exist, assuming abrupt termination and deleting lock file.") | ||
lockFile.deleteIfExists() | ||
} else { | ||
logger.lifecycle("Waiting for lock to be released...") | ||
var sleptMs: Long = alreadyWaited | ||
while (lockFile.exists()) { | ||
Thread.sleep(100) | ||
sleptMs += 100 | ||
if (sleptMs >= printInfoAfter && sleptMs % printInfoAfter == 0L) { | ||
logger.lifecycle( | ||
"Have been waiting on lock file '$lockFile' held by pid '$lockingProcessId' for ${sleptMs / 1000 / 60} minute(s).\n" + | ||
"If this persists for an unreasonable length of time, kill this process, run './gradlew --stop' and then try again.\n" + | ||
"If the problem persists, the lock file may need to be deleted manually." | ||
) | ||
} | ||
if (sleptMs >= timeoutMs) { | ||
throw PaperweightException("Have been waiting on lock file '$lockFile' for $sleptMs ms. Giving up as timeout is $timeoutMs ms.") | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!lockFile.parent.exists()) { | ||
lockFile.parent.createDirectories() | ||
} | ||
lockFile.writeText(currentPid.toString()) | ||
} |
Oops, something went wrong.