Skip to content

Commit

Permalink
Merge pull request #58 from embrace-io/thread-local
Browse files Browse the repository at this point in the history
Create property delegate function for ThreadLocal
  • Loading branch information
fractalwrench authored Nov 13, 2023
2 parents abaccb9 + f6a9ca8 commit ea815e8
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.annotation.VisibleForTesting
import io.embrace.android.embracesdk.internal.EmbraceSerializer
import io.embrace.android.embracesdk.internal.clock.Clock
import io.embrace.android.embracesdk.internal.utils.Uuid
import io.embrace.android.embracesdk.internal.utils.threadLocal
import io.embrace.android.embracesdk.logging.InternalEmbraceLogger
import io.embrace.android.embracesdk.payload.BackgroundActivityMessage
import io.embrace.android.embracesdk.payload.EventMessage
Expand Down Expand Up @@ -48,7 +49,7 @@ internal class EmbraceDeliveryCacheManager(
private const val TAG = "DeliveryCacheManager"
}

private val sessionMessageSerializer by lazy {
private val sessionMessageSerializer by threadLocal {
SessionMessageSerializer(serializer)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package io.embrace.android.embracesdk.internal

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonIOException
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import io.embrace.android.embracesdk.comms.api.EmbraceUrl
import io.embrace.android.embracesdk.comms.api.EmbraceUrlAdapter
import io.embrace.android.embracesdk.internal.utils.threadLocal
import io.embrace.android.embracesdk.logging.InternalStaticEmbraceLogger
import java.io.BufferedWriter
import java.lang.reflect.Type
Expand All @@ -17,34 +17,32 @@ import java.nio.charset.Charset
*/
internal class EmbraceSerializer {

private val gson: ThreadLocal<Gson> = object : ThreadLocal<Gson>() {
override fun initialValue(): Gson {
return GsonBuilder()
.registerTypeAdapter(EmbraceUrl::class.java, EmbraceUrlAdapter())
.create()
}
private val gson by threadLocal {
GsonBuilder()
.registerTypeAdapter(EmbraceUrl::class.java, EmbraceUrlAdapter())
.create()
}

fun <T> toJson(src: T): String {
return gson.get()?.toJson(src) ?: throw JsonIOException("Failed converting object to JSON.")
return gson.toJson(src) ?: throw JsonIOException("Failed converting object to JSON.")
}

fun <T> toJson(src: T, type: Type): String {
return gson.get()?.toJson(src, type)
return gson.toJson(src, type)
?: throw JsonIOException("Failed converting object to JSON.")
}

fun <T> fromJson(json: String, type: Type): T? {
return gson.get()?.fromJson(json, type)
return gson.fromJson(json, type)
}

fun <T> fromJson(json: String, clz: Class<T>): T? {
return gson.get()?.fromJson(json, clz)
return gson.fromJson(json, clz)
}

fun <T> writeToFile(any: T, clazz: Class<T>, bw: BufferedWriter): Boolean {
return try {
gson.get()?.toJson(any, clazz, JsonWriter(bw))
gson.toJson(any, clazz, JsonWriter(bw))
true
} catch (e: Exception) {
InternalStaticEmbraceLogger.logDebug("cannot write to bufferedWriter", e)
Expand All @@ -53,11 +51,11 @@ internal class EmbraceSerializer {
}

fun <T> loadObject(jsonReader: JsonReader, clazz: Class<T>): T? {
return gson.get()?.fromJson(jsonReader, clazz)
return gson.fromJson(jsonReader, clazz)
}

fun <T> bytesFromPayload(payload: T, clazz: Class<T>): ByteArray? {
val json: String? = gson.get()?.toJson(payload, clazz.genericSuperclass)
val json: String? = gson.toJson(payload, clazz.genericSuperclass)
return json?.toByteArray(Charset.forName("UTF-8"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.embrace.android.embracesdk.internal.utils

import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
* Syntactic sugar that makes it easier to define a property in Kotlin whose value is backed by
* a ThreadLocal.
*/
internal inline fun <reified T> threadLocal(
noinline provider: () -> T
): ReadOnlyProperty<Any?, T> = ThreadLocalDelegate(provider)

internal class ThreadLocalDelegate<T>(
provider: () -> T
) : ReadOnlyProperty<Any?, T> {

private val threadLocal: ThreadLocal<T> = object : ThreadLocal<T>() {
override fun initialValue(): T = provider()
}

override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return checkNotNull(threadLocal.get())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.embrace.android.embracesdk.internal.utils

import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Test
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

internal class ThreadLocalExtensionsTest {

private val id: Long by threadLocal {
Thread.currentThread().id
}

@Test
fun testThreadLocalProperties() {
val testThreadId = Thread.currentThread().id
assertEquals(id, testThreadId)

val latch = CountDownLatch(1)
Executors.newSingleThreadExecutor().submit {
assertEquals(id, Thread.currentThread().id)
assertNotEquals(id, testThreadId)
latch.countDown()
}
latch.await(1, TimeUnit.SECONDS)
}
}

0 comments on commit ea815e8

Please sign in to comment.