Skip to content

Commit c3499e6

Browse files
authored
Improve Resource Cleanup (#277)
* Improve resource cleanup on shutdown * review comments
1 parent d741721 commit c3499e6

File tree

9 files changed

+60
-37
lines changed

9 files changed

+60
-37
lines changed

core/src/main/java/com/segment/analytics/kotlin/core/Analytics.kt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -598,14 +598,31 @@ open class Analytics protected constructor(
598598
* Should only be called in containerized environments where you need to free resources like
599599
* CoroutineDispatchers and ExecutorService instances so they allow the container to shutdown
600600
* properly.
601+
*
602+
* @param waitForTasks if true, waits for all analyticsScope coroutines to complete before shutdown
601603
*/
604+
@JvmOverloads
602605
@OptIn(ExperimentalCoroutinesApi::class)
603-
fun shutdown() {
604-
(analyticsDispatcher as CloseableCoroutineDispatcher).close()
605-
(networkIODispatcher as CloseableCoroutineDispatcher).close()
606-
(fileIODispatcher as CloseableCoroutineDispatcher).close()
606+
fun shutdown(waitForTasks: Boolean = false) {
607+
timeline.applyClosure {
608+
it.shutdown()
609+
}
607610

608-
store.shutdown();
611+
val job = analyticsScope.coroutineContext[Job]
612+
job?.cancel()
613+
if (waitForTasks) {
614+
runBlocking {
615+
job?.join()
616+
}
617+
}
618+
619+
(analyticsDispatcher as? CloseableCoroutineDispatcher)?.close()
620+
(networkIODispatcher as? CloseableCoroutineDispatcher)?.close()
621+
(fileIODispatcher as? CloseableCoroutineDispatcher)?.close()
622+
623+
store.shutdown()
624+
625+
storage.close()
609626
}
610627

611628
/**
@@ -836,4 +853,4 @@ internal fun isAndroid(): Boolean {
836853
} catch (ignored: ClassNotFoundException) {
837854
false
838855
}
839-
}
856+
}

core/src/main/java/com/segment/analytics/kotlin/core/Storage.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ interface Storage {
9999
* Close and finish the current stream and start a new one
100100
*/
101101
suspend fun rollover()
102+
103+
/**
104+
* Close and cleanup storage resources
105+
*/
106+
fun close() {
107+
// empty body default
108+
}
102109
}
103110

104111
fun parseFilePaths(filePathStr: String?): List<String> {

core/src/main/java/com/segment/analytics/kotlin/core/platform/EventPipeline.kt

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ open class EventPipeline(
5353

5454
writeChannel = Channel(UNLIMITED)
5555
uploadChannel = Channel(UNLIMITED)
56-
57-
registerShutdownHook()
5856
}
5957

6058
fun put(event: BaseEvent) {
@@ -202,13 +200,4 @@ open class EventPipeline(
202200

203201
return shouldCleanup
204202
}
205-
206-
private fun registerShutdownHook() {
207-
// close the stream if the app shuts down
208-
Runtime.getRuntime().addShutdownHook(object : Thread() {
209-
override fun run() {
210-
this@EventPipeline.stop()
211-
}
212-
})
213-
}
214203
}

core/src/main/java/com/segment/analytics/kotlin/core/platform/Plugin.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ interface Plugin {
4343
fun update(settings: Settings, type: UpdateType) {
4444
// empty body default
4545
}
46+
47+
fun shutdown() {
48+
// empty body default
49+
}
4650
}
4751

4852
interface VersionedPlugin {

core/src/main/java/com/segment/analytics/kotlin/core/platform/plugins/SegmentDestination.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ class SegmentDestination: DestinationPlugin(), VersionedPlugin, Subscriber {
111111
pipeline?.flush()
112112
}
113113

114+
override fun shutdown() {
115+
pipeline?.stop()
116+
}
117+
114118
override fun version(): String {
115119
return Constants.LIBRARY_VERSION
116120
}

core/src/main/java/com/segment/analytics/kotlin/core/utilities/EventStream.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ open class FileEventStream(
160160

161161
init {
162162
createDirectory(directory)
163-
registerShutdownHook()
164163
}
165164

166165
protected open var fs: FileOutputStream? = null
@@ -239,13 +238,4 @@ open class FileEventStream(
239238
val file = File(source)
240239
return if (file.exists()) FileInputStream(file) else null
241240
}
242-
243-
private fun registerShutdownHook() {
244-
// close the stream if the app shuts down
245-
Runtime.getRuntime().addShutdownHook(object : Thread() {
246-
override fun run() {
247-
fs?.close()
248-
}
249-
})
250-
}
251241
}

core/src/main/java/com/segment/analytics/kotlin/core/utilities/EventsFileManager.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ class EventsFileManager(
4141

4242
init {
4343
createDirectory(directory)
44-
registerShutdownHook()
4544
}
4645

4746
private val fileIndexKey = if(subject == null) "segment.events.file.index.$writeKey" else "segment.events.file.index.$writeKey.$subject"
@@ -170,15 +169,6 @@ class EventsFileManager(
170169
curFile = null
171170
}
172171

173-
private fun registerShutdownHook() {
174-
// close the stream if the app shuts down
175-
Runtime.getRuntime().addShutdownHook(object : Thread() {
176-
override fun run() {
177-
os?.close()
178-
}
179-
})
180-
}
181-
182172
private suspend fun withLock(block: () -> Unit) {
183173
semaphore.acquire()
184174
block()

core/src/main/java/com/segment/analytics/kotlin/core/utilities/StorageImpl.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ open class StorageImpl(
195195
block()
196196
semaphore.release()
197197
}
198+
199+
override fun close() {
200+
if (eventStream is FileEventStream) {
201+
(eventStream as FileEventStream).close()
202+
}
203+
}
198204
}
199205

200206
object ConcreteStorageProvider : StorageProvider {

core/src/test/kotlin/com/segment/analytics/kotlin/core/AnalyticsTests.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,22 @@ class AnalyticsTests {
947947
assertEquals(2, files.size)
948948
}
949949

950+
@Test
951+
fun `shutdown calls shutdown on all plugins`() {
952+
val config = Configuration(
953+
writeKey = "test-shutdown",
954+
application = "TestApp"
955+
)
956+
val testScope = TestScope(testDispatcher)
957+
val testAnalytics = testAnalytics(config, testScope, testDispatcher)
958+
val mockPlugin = spyk<StubPlugin>()
959+
testAnalytics.add(mockPlugin)
960+
961+
testAnalytics.shutdown()
962+
963+
verify { mockPlugin.shutdown() }
964+
}
965+
950966
@Test
951967
fun `purgeStorage clears storage`() = runTest {
952968
analytics.track("test")

0 commit comments

Comments
 (0)