# Leak investigation: Rx disposal race in SQLDelight

DevFeed: [Leak investigation: Rx disposal race in SQLDelight](<https://devfeed.tech/articles/leak-investigation-rx-disposal-race-in-sqldelight-25862.md>)

Original publisher: [Read original article](<https://dev.to/pyricau/leak-investigation-rx-disposal-race-in-sqldelight-3n06>)

Author: Py ⚔

Published: 2021-05-17T22:10:41Z

Content type: tutorial

Language: en

Sources: [Py ⚔](<https://devfeed.tech/sources/py.md>)

Topics: [RxJava](<https://devfeed.tech/topics/rxjava.md>), [Code](<https://devfeed.tech/topics/code.md>), [implementation](<https://devfeed.tech/topics/implementation.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [coding](<https://devfeed.tech/tags/coding.md>), [community](<https://devfeed.tech/tags/community.md>), [development](<https://devfeed.tech/tags/development.md>), [engineering](<https://devfeed.tech/tags/engineering.md>), [implementation](<https://devfeed.tech/tags/implementation.md>), [inclusive](<https://devfeed.tech/tags/inclusive.md>), [leak](<https://devfeed.tech/tags/leak.md>), [rxjava](<https://devfeed.tech/tags/rxjava.md>), [software](<https://devfeed.tech/tags/software.md>), [sqldelight](<https://devfeed.tech/tags/sqldelight.md>), [thread](<https://devfeed.tech/tags/thread.md>)

## AI overview

This article investigates a memory leak caused by a disposal race in SQLDelight's RxJava integration. The implementation sets the disposable before adding the query listener, so a subscription that is already disposed can add a listener that is never removed. The article recommends checking listener and disposable ordering and avoiding unnecessary scheduler calls for observables originating from Observable.create().

## Source excerpt

Header image: The In-Between by Romain Guy. In this blog we'll look into how an easy mistake when using Observable.create() can lead to subtle leaks. I recently investigated the following leak, which I couldn't reproduce systematically: ┬─── ... ├─ com.example.hockey.PlayerQueries$selectAllQuery instance │ ↓ Query.listeners │ ~~~~~~~~~ ├─ java.util.concurrent.CopyOnWriteArrayList instance │ ↓ CopyOnWriteArrayList.array │ ~~~~~ ├─ java.lang.Object[] array │ ↓ Object[].[0] │ ~~~ ├─ sqldelight.runtime.rx.QueryListenerAndDisposable instance │ Retaining 4.3 kB in 56 objects │ ↓ QueryListenerAndDisposable.emitter │ ~~~~~~~ ... RxJava observer chain ├─ com.example.hockey.PlayersView$onAttachedToWindow$1 instance │ Anonymous class implementing io.reactivex.functions.Function │ ↓ PlayersView$onAttachedToWindow$1.this$0 │ ~~~~~~ ╰-> com.example.hockey.view.PlayersView instance Leaking: YES (View.mContext references a destroyed activity) In the above leaktrace, PlayerQueries$selectAllQuery is a generated SQLDelight query. Our PlayersView is listening for updates to that query while the view is attached by leveraging Query.asObservable(). Once the view is detached, the observable chain is disposed and the query is expected to let go of the corresponding listener. I inspected the heap dump and found that the view was indeed detached, the observable chain was correctly disposed, and yet the QueryListenerAndDisposable listener had not been removed from the query. Let's look at the Query.asObservable() implementation: fun <T : Any> Query<T>.asObservable(): Observable<Query<T>> { return Observable.create(QueryOnSubscribe(this)) } private class QueryOnSubscribe<T : Any>( private val query: Query<T> ) : ObservableOnSubscribe<Query<T>> { override fun subscribe(emitter: ObservableEmitter<Query<T>>) { val listener = QueryListenerAndDisposable(emitter, query) emitter.setDisposable(listener) query.addListener(listener) emitter.onNext(query) } } private class QueryListenerAndDisposable<T : A