Skip to content

Commit 9a0f7d8

Browse files
jennantillajinliu9508
authored andcommitted
Merge pull request #1876 from OneSignal/fix/concurrent_modification_exception
Fix: Add synchronized blocks to prevent ConcurrentModificationException
2 parents c5ed180 + 9a28fd0 commit 9a0f7d8

File tree

3 files changed

+67
-51
lines changed

3 files changed

+67
-51
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/Model.kt

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ open class Model(
5151
* specified, must also specify [_parentModel]
5252
*/
5353
private val _parentProperty: String? = null,
54+
private val initializationLock: Any = Any(),
5455
) : IEventNotifier<IModelChangedHandler> {
5556
/**
5657
* A unique identifier for this model.
@@ -123,19 +124,25 @@ open class Model(
123124
id: String?,
124125
model: Model,
125126
) {
126-
data.clear()
127+
val newData = Collections.synchronizedMap(mutableMapOf<String, Any?>())
128+
127129
for (item in model.data) {
128130
if (item.value is Model) {
129131
val childModel = item.value as Model
130132
childModel._parentModel = this
131-
data[item.key] = childModel
133+
newData[item.key] = childModel
132134
} else {
133-
data[item.key] = item.value
135+
newData[item.key] = item.value
134136
}
135137
}
136138

137139
if (id != null) {
138-
data[::id.name] = id
140+
newData[::id.name] = id
141+
}
142+
143+
synchronized(initializationLock) {
144+
data.clear()
145+
data.putAll(newData)
139146
}
140147
}
141148

@@ -429,19 +436,21 @@ open class Model(
429436
tag: String = ModelChangeTags.NORMAL,
430437
forceChange: Boolean = false,
431438
) {
432-
val oldValue = data[name]
439+
synchronized(data) {
440+
val oldValue = data[name]
433441

434-
if (oldValue == value && !forceChange) {
435-
return
436-
}
442+
if (oldValue == value && !forceChange) {
443+
return
444+
}
437445

438-
if (value != null) {
439-
data[name] = value
440-
} else if (data.containsKey(name)) {
441-
data.remove(name)
442-
}
446+
if (value != null) {
447+
data[name] = value
448+
} else if (data.containsKey(name)) {
449+
data.remove(name)
450+
}
443451

444-
notifyChanged(name, name, tag, oldValue, value)
452+
notifyChanged(name, name, tag, oldValue, value)
453+
}
445454
}
446455

447456
/**
@@ -627,12 +636,14 @@ open class Model(
627636
name: String,
628637
create: (() -> Any?)? = null,
629638
): Any? {
630-
return if (data.containsKey(name) || create == null) {
631-
data[name]
632-
} else {
633-
val defaultValue = create()
634-
data[name] = defaultValue as Any?
635-
defaultValue
639+
synchronized(data) {
640+
return if (data.containsKey(name) || create == null) {
641+
data[name]
642+
} else {
643+
val defaultValue = create()
644+
data[name] = defaultValue as Any?
645+
defaultValue
646+
}
636647
}
637648
}
638649

@@ -660,29 +671,31 @@ open class Model(
660671
* @return The resulting [JSONObject].
661672
*/
662673
fun toJSON(): JSONObject {
663-
val jsonObject = JSONObject()
664-
for (kvp in data) {
665-
when (val value = kvp.value) {
666-
is Model -> {
667-
jsonObject.put(kvp.key, value.toJSON())
668-
}
669-
is List<*> -> {
670-
val jsonArray = JSONArray()
671-
for (arrayItem in value) {
672-
if (arrayItem is Model) {
673-
jsonArray.put(arrayItem.toJSON())
674-
} else {
675-
jsonArray.put(arrayItem)
674+
synchronized(initializationLock) {
675+
val jsonObject = JSONObject()
676+
for (kvp in data) {
677+
when (val value = kvp.value) {
678+
is Model -> {
679+
jsonObject.put(kvp.key, value.toJSON())
680+
}
681+
is List<*> -> {
682+
val jsonArray = JSONArray()
683+
for (arrayItem in value) {
684+
if (arrayItem is Model) {
685+
jsonArray.put(arrayItem.toJSON())
686+
} else {
687+
jsonArray.put(arrayItem)
688+
}
676689
}
690+
jsonObject.put(kvp.key, jsonArray)
691+
}
692+
else -> {
693+
jsonObject.put(kvp.key, value)
677694
}
678-
jsonObject.put(kvp.key, jsonArray)
679-
}
680-
else -> {
681-
jsonObject.put(kvp.key, value)
682695
}
683696
}
697+
return jsonObject
684698
}
685-
return jsonObject
686699
}
687700

688701
override fun subscribe(handler: IModelChangedHandler) = changeNotifier.subscribe(handler)

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/ModelStore.kt

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ abstract class ModelStore<TModel>(
4545
removeItem(oldModel, tag)
4646
}
4747

48-
addItem(model, tag)
48+
addItem(model, tag)
49+
}
4950
}
5051

5152
override fun add(
@@ -58,7 +59,8 @@ abstract class ModelStore<TModel>(
5859
removeItem(oldModel, tag)
5960
}
6061

61-
addItem(model, tag, index)
62+
addItem(model, tag, index)
63+
}
6264
}
6365

6466
override fun list(): Collection<TModel> {
@@ -92,16 +94,17 @@ abstract class ModelStore<TModel>(
9294
) {
9395
clear(tag)
9496

95-
for (model in models) {
96-
add(model, tag)
97+
for (model in models) {
98+
add(model, tag)
99+
}
97100
}
98101
}
99102

100103
override fun clear(tag: String) {
101104
val localList = models.toList()
102105
models.clear()
103106

104-
persist()
107+
persist()
105108

106109
for (item in localList) {
107110
// no longer listen for changes to this model
@@ -121,10 +124,10 @@ abstract class ModelStore<TModel>(
121124
models.add(model)
122125
}
123126

124-
// listen for changes to this model
125-
model.subscribe(this)
127+
// listen for changes to this model
128+
model.subscribe(this)
126129

127-
persist()
130+
persist()
128131

129132
changeSubscription.fire { it.onModelAdded(model, tag) }
130133
}
@@ -135,10 +138,10 @@ abstract class ModelStore<TModel>(
135138
) {
136139
models.remove(model)
137140

138-
// no longer listen for changes to this model
139-
model.unsubscribe(this)
141+
// no longer listen for changes to this model
142+
model.unsubscribe(this)
140143

141-
persist()
144+
persist()
142145

143146
changeSubscription.fire { it.onModelRemoved(model, tag) }
144147
}
@@ -162,8 +165,6 @@ abstract class ModelStore<TModel>(
162165
for (model in models) {
163166
jsonArray.put(model.toJSON())
164167
}
165-
166-
_prefs.saveString(PreferenceStores.ONESIGNAL, PreferenceOneSignalKeys.MODEL_STORE_PREFIX + name, jsonArray.toString())
167168
}
168169
}
169170

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/common/modeling/SingletonModelStore.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ open class SingletonModelStore<TModel>(
1212
private val changeSubscription: EventProducer<ISingletonModelStoreChangeHandler<TModel>> = EventProducer()
1313
private val singletonId: String = "-singleton-"
1414

15+
private val replaceLock = Any()
16+
1517
init {
1618
store.subscribe(this)
1719
}

0 commit comments

Comments
 (0)