Skip to content

Commit 2f4ed4e

Browse files
Handle possible OverlappingFileLockExceptions for cache file (#218)
* Handle possible OverlappingFileLockExceptions for cache file * code style fix * small improvement in a comment
1 parent 87940d7 commit 2f4ed4e

File tree

2 files changed

+46
-24
lines changed

2 files changed

+46
-24
lines changed

codec-registration-checker-compiler-plugin/src/main/scala/org/virtuslab/ash/CodecRegistrationCheckerCompilerPlugin.scala

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import java.io.FileNotFoundException
55
import java.io.IOException
66
import java.io.RandomAccessFile
77
import java.nio.ByteBuffer
8+
import java.nio.channels.OverlappingFileLockException
89
import java.nio.charset.StandardCharsets
910

1011
import scala.tools.nsc.Global
@@ -30,35 +31,54 @@ class CodecRegistrationCheckerCompilerPlugin(override val global: Global) extend
3031

3132
options.filterNot(_.startsWith("-")).headOption match {
3233
case Some(path) =>
33-
try {
34-
val cacheFile = new File(path + File.separator + directClassDescendantsCacheFileName)
35-
cacheFile.getCanonicalPath
36-
pluginOptions.directClassDescendantsCacheFile = cacheFile
37-
pluginOptions.oldParentChildFQCNPairs = {
38-
val raf = new RandomAccessFile(cacheFile, "rw")
39-
try {
40-
val channel = raf.getChannel
41-
val lock = channel.lock()
34+
/*
35+
Below retry-loop is needed because of possible OverlappingFileLockException that might
36+
occur if codec-registration-checker-plugin is enabled in multiple projects (modules)
37+
and these projects are compiled in parallel by `sbt compile`. As all projects/modules
38+
share the same cache file - `channel.lock()` might cause mentioned exception.
39+
*/
40+
var shouldInitialize = false
41+
var loopCount = 0
42+
val maxTries = 5
43+
while (loopCount < maxTries && !shouldInitialize) {
44+
try {
45+
val cacheFile = new File(path + File.separator + directClassDescendantsCacheFileName)
46+
cacheFile.getCanonicalPath
47+
pluginOptions.directClassDescendantsCacheFile = cacheFile
48+
pluginOptions.oldParentChildFQCNPairs = {
49+
val raf = new RandomAccessFile(cacheFile, "rw")
4250
try {
43-
val buffer = ByteBuffer.allocate(channel.size().toInt)
44-
channel.read(buffer)
45-
CodecRegistrationCheckerCompilerPlugin.parseCacheFile(buffer.rewind())
51+
val channel = raf.getChannel
52+
val lock = channel.lock()
53+
try {
54+
val buffer = ByteBuffer.allocate(channel.size().toInt)
55+
channel.read(buffer)
56+
CodecRegistrationCheckerCompilerPlugin.parseCacheFile(buffer.rewind())
57+
} finally {
58+
lock.close()
59+
}
4660
} finally {
47-
lock.close()
61+
raf.close()
4862
}
49-
} finally {
50-
raf.close()
5163
}
64+
shouldInitialize = true
65+
} catch {
66+
case _: FileNotFoundException =>
67+
pluginOptions.oldParentChildFQCNPairs = Nil
68+
shouldInitialize = true
69+
case e: OverlappingFileLockException =>
70+
if (loopCount + 1 == maxTries)
71+
error(s"OverlappingFileLockException thrown, message: ${e.getMessage}")
72+
else
73+
Thread.sleep(20)
74+
case e: IOException =>
75+
error(s"IOException thrown, message: ${e.getMessage}")
76+
case e: RuntimeException =>
77+
error(s"RuntimeException thrown, message: ${e.getMessage}")
5278
}
53-
true
54-
} catch {
55-
case _: FileNotFoundException =>
56-
pluginOptions.oldParentChildFQCNPairs = Nil
57-
true
58-
case e @ (_: IOException | _: RuntimeException) =>
59-
error(s"Exception thrown, message: ${e.getMessage}")
60-
false
79+
loopCount += 1
6180
}
81+
shouldInitialize
6282
case None =>
6383
error("No directory for saving cache file specified")
6484
false

docs/GUIDE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ This option disables detection of messages/events/state based on return type of
119119
`Compile / scalacOptions += "-P:serializability-checker-plugin:--disable-detection-higher-order-function"`<br><br>
120120

121121
### Codec Registration Checker Compiler Plugin
122-
Before using this compiler plugin, make sure that you are using both [annotations](#annotations) properly. If so &mdash; the plugin can be used right away. This plugin checks whether classes marked with serializability trait are being referenced in a marked serializer, which ensures that codecs will be registered in runtime.<br><br>
122+
Before using this compiler plugin, make sure that you are using both [annotations](#annotations) properly. If so &mdash; the plugin can be used right away. This plugin checks whether classes marked with serializability trait are being referenced in a marked serializer, which ensures that codecs will be registered in runtime.
123+
124+
**Note** - Codec Registration Checker Compiler Plugin is useful only in projects (modules) where the [@Serializer](#Serializer) annotation is used. Therefore, if you are using Akka Serialization Helper in multiple modules but in fact use `@Serializer` annotation in only one module, you might disable this plugin in all other modules except the one where `@Serializer` annotation is used.<br><br>
123125
Codec Registration Checker Compiler Plugin does not need additional configuration, but you can change default configurations as explained below:
124126

125127
- `--disable`

0 commit comments

Comments
 (0)