Skip to content

Commit 95db8a4

Browse files
maropusrowen
authored andcommitted
[SPARK-15528][SQL] Fix race condition in NumberConverter
## What changes were proposed in this pull request? A local variable in NumberConverter is wrongly shared between threads. This pr fixes the race condition. ## How was this patch tested? Manually checked. Author: Takeshi YAMAMURO <linguin.m.s@gmail.com> Closes apache#13391 from maropu/SPARK-15528.
1 parent 6878f3e commit 95db8a4

1 file changed

Lines changed: 17 additions & 21 deletions

File tree

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/NumberConverter.scala

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ import org.apache.spark.unsafe.types.UTF8String
2121

2222
object NumberConverter {
2323

24-
private val value = new Array[Byte](64)
25-
2624
/**
2725
* Divide x by m as if x is an unsigned 64-bit integer. Examples:
2826
* unsignedLongDiv(-1, 2) == Long.MAX_VALUE unsignedLongDiv(6, 3) == 2
@@ -49,7 +47,7 @@ object NumberConverter {
4947
* @param v is treated as an unsigned 64-bit integer
5048
* @param radix must be between MIN_RADIX and MAX_RADIX
5149
*/
52-
private def decode(v: Long, radix: Int): Unit = {
50+
private def decode(v: Long, radix: Int, value: Array[Byte]): Unit = {
5351
var tmpV = v
5452
java.util.Arrays.fill(value, 0.asInstanceOf[Byte])
5553
var i = value.length - 1
@@ -69,11 +67,9 @@ object NumberConverter {
6967
* @param fromPos is the first element that should be considered
7068
* @return the result should be treated as an unsigned 64-bit integer.
7169
*/
72-
private def encode(radix: Int, fromPos: Int): Long = {
70+
private def encode(radix: Int, fromPos: Int, value: Array[Byte]): Long = {
7371
var v: Long = 0L
7472
val bound = unsignedLongDiv(-1 - radix, radix) // Possible overflow once
75-
// val
76-
// exceeds this value
7773
var i = fromPos
7874
while (i < value.length && value(i) >= 0) {
7975
if (v >= bound) {
@@ -94,7 +90,7 @@ object NumberConverter {
9490
* @param radix must be between MIN_RADIX and MAX_RADIX
9591
* @param fromPos is the first nonzero element
9692
*/
97-
private def byte2char(radix: Int, fromPos: Int): Unit = {
93+
private def byte2char(radix: Int, fromPos: Int, value: Array[Byte]): Unit = {
9894
var i = fromPos
9995
while (i < value.length) {
10096
value(i) = Character.toUpperCase(Character.forDigit(value(i), radix)).asInstanceOf[Byte]
@@ -109,9 +105,9 @@ object NumberConverter {
109105
* @param radix must be between MIN_RADIX and MAX_RADIX
110106
* @param fromPos is the first nonzero element
111107
*/
112-
private def char2byte(radix: Int, fromPos: Int): Unit = {
108+
private def char2byte(radix: Int, fromPos: Int, value: Array[Byte]): Unit = {
113109
var i = fromPos
114-
while ( i < value.length) {
110+
while (i < value.length) {
115111
value(i) = Character.digit(value(i), radix).asInstanceOf[Byte]
116112
i += 1
117113
}
@@ -124,8 +120,8 @@ object NumberConverter {
124120
*/
125121
def convert(n: Array[Byte], fromBase: Int, toBase: Int ): UTF8String = {
126122
if (fromBase < Character.MIN_RADIX || fromBase > Character.MAX_RADIX
127-
|| Math.abs(toBase) < Character.MIN_RADIX
128-
|| Math.abs(toBase) > Character.MAX_RADIX) {
123+
|| Math.abs(toBase) < Character.MIN_RADIX
124+
|| Math.abs(toBase) > Character.MAX_RADIX) {
129125
return null
130126
}
131127

@@ -136,15 +132,16 @@ object NumberConverter {
136132
var (negative, first) = if (n(0) == '-') (true, 1) else (false, 0)
137133

138134
// Copy the digits in the right side of the array
135+
val temp = new Array[Byte](64)
139136
var i = 1
140137
while (i <= n.length - first) {
141-
value(value.length - i) = n(n.length - i)
138+
temp(temp.length - i) = n(n.length - i)
142139
i += 1
143140
}
144-
char2byte(fromBase, value.length - n.length + first)
141+
char2byte(fromBase, temp.length - n.length + first, temp)
145142

146143
// Do the conversion by going through a 64 bit integer
147-
var v = encode(fromBase, value.length - n.length + first)
144+
var v = encode(fromBase, temp.length - n.length + first, temp)
148145
if (negative && toBase > 0) {
149146
if (v < 0) {
150147
v = -1
@@ -156,21 +153,20 @@ object NumberConverter {
156153
v = -v
157154
negative = true
158155
}
159-
decode(v, Math.abs(toBase))
156+
decode(v, Math.abs(toBase), temp)
160157

161158
// Find the first non-zero digit or the last digits if all are zero.
162159
val firstNonZeroPos = {
163-
val firstNonZero = value.indexWhere( _ != 0)
164-
if (firstNonZero != -1) firstNonZero else value.length - 1
160+
val firstNonZero = temp.indexWhere( _ != 0)
161+
if (firstNonZero != -1) firstNonZero else temp.length - 1
165162
}
166-
167-
byte2char(Math.abs(toBase), firstNonZeroPos)
163+
byte2char(Math.abs(toBase), firstNonZeroPos, temp)
168164

169165
var resultStartPos = firstNonZeroPos
170166
if (negative && toBase < 0) {
171167
resultStartPos = firstNonZeroPos - 1
172-
value(resultStartPos) = '-'
168+
temp(resultStartPos) = '-'
173169
}
174-
UTF8String.fromBytes(java.util.Arrays.copyOfRange(value, resultStartPos, value.length))
170+
UTF8String.fromBytes(java.util.Arrays.copyOfRange(temp, resultStartPos, temp.length))
175171
}
176172
}

0 commit comments

Comments
 (0)