Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] thread safety with EventProducer's fire events #2034

Merged
merged 1 commit into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ open class EventProducer<THandler> : IEventNotifier<THandler> {
* @param callback The callback will be invoked for each subscribed handler, allowing you to call the handler.
*/
fun fire(callback: (THandler) -> Unit) {
val localList = subscribers.toList()
val localList = synchronized(subscribers) { subscribers.toList() }
for (s in localList) {
callback(s)
}
Expand All @@ -60,7 +60,7 @@ open class EventProducer<THandler> : IEventNotifier<THandler> {
*/
fun fireOnMain(callback: (THandler) -> Unit) {
suspendifyOnMain {
val localList = subscribers.toList()
val localList = synchronized(subscribers) { subscribers.toList() }
for (s in localList) {
callback(s)
}
Expand All @@ -74,7 +74,7 @@ open class EventProducer<THandler> : IEventNotifier<THandler> {
* @param callback The callback will be invoked for each subscribed handler, allowing you to call the handler.
*/
suspend fun suspendingFire(callback: suspend (THandler) -> Unit) {
val localList = subscribers.toList()
val localList = synchronized(subscribers) { subscribers.toList() }
for (s in localList) {
callback(s)
}
Expand All @@ -88,7 +88,7 @@ open class EventProducer<THandler> : IEventNotifier<THandler> {
*/
suspend fun suspendingFireOnMain(callback: suspend (THandler) -> Unit) {
withContext(Dispatchers.Main) {
val localList = subscribers.toList()
val localList = synchronized(subscribers) { subscribers.toList() }
for (s in localList) {
callback(s)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.onesignal.common

import com.onesignal.common.events.EventProducer
import io.kotest.core.spec.style.FunSpec
import kotlin.concurrent.thread

class EventProducerTest : FunSpec({

fun modifyingSubscribersThread(eventProducer: EventProducer<Boolean>): Thread {
return thread(start = true) {
repeat(10_000) {
eventProducer.subscribe(true)
eventProducer.unsubscribe(true)
}
}
}

test("fire is thread safe") {
val eventProducer = EventProducer<Boolean>()
val modifyingSubscribersThread = modifyingSubscribersThread(eventProducer)

repeat(10_000) {
eventProducer.fire { }
}

modifyingSubscribersThread.join()
}

test("suspendingFire is thread safe") {
val eventProducer = EventProducer<Boolean>()
val modifyingSubscribersThread = modifyingSubscribersThread(eventProducer)

repeat(10_000) {
eventProducer.suspendingFire { }
}

modifyingSubscribersThread.join()
}

test("hasSubscribers is thread safe") {
val eventProducer = EventProducer<Boolean>()
val modifyingSubscribersThread = modifyingSubscribersThread(eventProducer)

repeat(10_000) {
eventProducer.hasSubscribers
}

modifyingSubscribersThread.join()
}
})
Loading