1
1
package scala .runtime
2
2
3
- import scala .collection .mutable
4
- import java .nio .file .Path
3
+ import scala .collection .mutable .{BitSet , AnyRefMap }
5
4
import scala .collection .concurrent .TrieMap
6
- import java .nio .file .Paths
7
5
import java .nio .file .Files
8
- import java .nio .file .StandardOpenOption
9
6
import java .io .FileWriter
10
7
import java .io .File
11
8
12
9
object Invoker {
13
10
private val runtimeUUID = java.util.UUID .randomUUID()
14
11
15
12
private val MeasurementsPrefix = " scoverage.measurements."
16
- private val threadFiles = new ThreadLocal [mutable.HashMap [String , FileWriter ]]
17
-
18
- // For each data directory we maintain a thread-safe set tracking the ids
19
- // that we've already seen and recorded. We're using a map as a set, so we
20
- // only care about its keys and can ignore its values.
21
- private val dataDirToIds = TrieMap .empty[String , TrieMap [Int , Any ]]
13
+ private val threadFiles = new ThreadLocal [AnyRefMap [String , FileWriter ]]
14
+ private val dataDirToSet = TrieMap .empty[String , BitSet ]
22
15
23
16
/** We record that the given id has been invoked by appending its id to the coverage data file.
24
17
*
@@ -34,40 +27,24 @@ object Invoker {
34
27
* @param dataDir
35
28
* the directory where the measurement data is held
36
29
*/
37
- def invoked (id : Int , dataDir : String ): Unit = {
38
- // [sam] we can do this simple check to save writing out to a file.
39
- // This won't work across JVMs but since there's no harm in writing out the same id multiple
40
- // times since for coverage we only care about 1 or more, (it just slows things down to
41
- // do it more than once), anything we can do to help is good. This helps especially with code
42
- // that is executed many times quickly, eg tight loops.
43
- if (! dataDirToIds.contains(dataDir)) {
44
- // Guard against SI-7943: "TrieMap method getOrElseUpdate is not thread-safe".
45
- dataDirToIds.synchronized {
46
- if (! dataDirToIds.contains(dataDir)) {
47
- dataDirToIds(dataDir) = TrieMap .empty[Int , Any ]
48
- }
49
- }
50
- }
51
- val ids = dataDirToIds(dataDir)
52
- if (! ids.contains(id)) {
53
- // Each thread writes to a separate measurement file, to reduce contention
54
- // and because file appends via FileWriter are not atomic on Windows.
55
- var files = threadFiles.get()
56
- if (files == null ) {
57
- files = mutable.HashMap .empty[String , FileWriter ]
58
- threadFiles.set(files)
30
+ def invoked (id : Int , dataDir : String ): Unit =
31
+ val set = dataDirToSet.getOrElseUpdate(dataDir, BitSet .empty)
32
+ if ! set.contains(id) then
33
+ val added = set.synchronized {
34
+ set.add(id)
59
35
}
60
- val writer = files.getOrElseUpdate(
61
- dataDir,
62
- new FileWriter (measurementFile(dataDir), true )
63
- )
64
-
65
- writer.append(Integer .toString(id))
66
- writer.append(" \n " )
67
- writer.flush()
68
- ids.put(id, ())
69
- }
70
- }
36
+ if added then
37
+ var writers = threadFiles.get()
38
+ if writers == null then
39
+ writers = AnyRefMap .empty
40
+ threadFiles.set(writers)
41
+ val writer = writers.getOrElseUpdate(
42
+ dataDir,
43
+ FileWriter (measurementFile(dataDir), true )
44
+ )
45
+ writer.write(Integer .toString(id))
46
+ writer.write('\n ' )
47
+ writer.flush()
71
48
72
49
def measurementFile (dataDir : String ): File = new File (
73
50
dataDir,
0 commit comments