Skip to content

Commit 00e943e

Browse files
authored
Replace synchronized usage in library with kotlinx-atomicfu (#2808)
1 parent 0afb23e commit 00e943e

27 files changed

Lines changed: 190 additions & 150 deletions

File tree

app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -408,13 +408,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
408408
return true
409409
}
410410

411-
synchronized(apis) {
412-
for (api in apis) {
413-
if (str.startsWith(api.mainUrl)) {
414-
loadResult(str, api.name, "")
415-
return true
416-
}
417-
}
411+
val matchedApi = apis.filter { str.startsWith(it.mainUrl) }.firstOrNull()
412+
if (matchedApi != null) {
413+
loadResult(str, matchedApi.name, "")
414+
return true
418415
}
419416
}
420417
}
@@ -809,12 +806,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
809806
}
810807
}
811808

812-
813809
private val pluginsLock = Mutex()
814810
private fun onAllPluginsLoaded(success: Boolean = false) {
815811
ioSafe {
816812
pluginsLock.withLock {
817-
synchronized(allProviders) {
813+
allProviders.withLock {
818814
// Load cloned sites after plugins have been loaded since clones depend on plugins.
819815
try {
820816
getKey<Array<SettingsGeneral.CustomSite>>(USER_PROVIDER_API)?.let { list ->
@@ -1657,9 +1653,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
16571653
ioSafe {
16581654
initAll()
16591655
// No duplicates (which can happen by registerMainAPI)
1660-
apis = synchronized(allProviders) {
1661-
allProviders.distinctBy { it }
1662-
}
1656+
apis = allProviders.distinctBy { it }
16631657
}
16641658

16651659
// val navView: BottomNavigationView = findViewById(R.id.nav_view)
@@ -1967,7 +1961,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
19671961

19681962
if (BuildConfig.DEBUG) {
19691963
var providersAndroidManifestString = "Current androidmanifest should be:\n"
1970-
synchronized(allProviders) {
1964+
allProviders.withLock {
19711965
for (api in allProviders) {
19721966
providersAndroidManifestString += "<data android:scheme=\"https\" android:host=\"${
19731967
api.mainUrl.removePrefix(

app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
3232
import com.lagradost.cloudstream3.mvvm.logError
3333
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
3434
import com.lagradost.cloudstream3.ui.result.ResultEpisode
35+
import com.lagradost.cloudstream3.utils.Coroutines.atomicListOf
3536
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
36-
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
3737
import com.lagradost.cloudstream3.utils.ExtractorLinkType
3838
import com.lagradost.cloudstream3.utils.UiText
3939
import kotlinx.coroutines.Dispatchers
@@ -43,7 +43,7 @@ import java.util.concurrent.FutureTask
4343
import kotlin.reflect.jvm.jvmName
4444

4545
object VideoClickActionHolder {
46-
val allVideoClickActions = threadSafeListOf(
46+
val allVideoClickActions = atomicListOf(
4747
// Default
4848
PlayInBrowserAction(),
4949
CopyClipboardAction(),

app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import com.lagradost.cloudstream3.actions.VideoClickAction
77
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
88
import kotlin.Throws
99

10-
1110
abstract class Plugin : BasePlugin() {
1211
/**
1312
* Called when your Plugin is loaded
@@ -26,9 +25,7 @@ abstract class Plugin : BasePlugin() {
2625
fun registerVideoClickAction(element: VideoClickAction) {
2726
Log.i(PLUGIN_TAG, "Adding ${element.name} VideoClickAction")
2827
element.sourcePlugin = this.filename
29-
synchronized(VideoClickActionHolder.allVideoClickActions) {
30-
VideoClickActionHolder.allVideoClickActions.add(element)
31-
}
28+
VideoClickActionHolder.allVideoClickActions.add(element)
3229
}
3330

3431
/**
@@ -40,4 +37,4 @@ abstract class Plugin : BasePlugin() {
4037
* This will add a button in the settings allowing you to add custom settings
4138
*/
4239
var openSettings: ((context: Context) -> Unit)? = null
43-
}
40+
}

app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,15 @@ object PluginManager {
651651
context.resources.configuration
652652
)
653653
}
654-
plugins[filePath] = pluginInstance
655-
classLoaders[loader] = pluginInstance
656-
urlPlugins[data.url ?: filePath] = pluginInstance
654+
synchronized(plugins) {
655+
plugins[filePath] = pluginInstance
656+
}
657+
synchronized(classLoaders) {
658+
classLoaders[loader] = pluginInstance
659+
}
660+
synchronized(urlPlugins) {
661+
urlPlugins[data.url ?: filePath] = pluginInstance
662+
}
657663
if (pluginInstance is Plugin) {
658664
pluginInstance.load(context)
659665
} else {
@@ -689,21 +695,20 @@ object PluginManager {
689695
}
690696

691697
// remove all registered apis
692-
synchronized(APIHolder.apis) {
693-
APIHolder.apis.filter { api -> api.sourcePlugin == plugin.filename }.forEach {
694-
removePluginMapping(it)
695-
}
698+
APIHolder.apis.filter { api -> api.sourcePlugin == plugin.filename }.forEach {
699+
removePluginMapping(it)
696700
}
697-
synchronized(APIHolder.allProviders) {
698-
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.filename }
701+
702+
APIHolder.allProviders.withLock {
703+
APIHolder.allProviders.removeAll { provider -> provider.sourcePlugin == plugin.filename }
699704
}
700705

701-
synchronized(extractorApis) {
702-
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.filename }
706+
extractorApis.withLock {
707+
extractorApis.removeAll { provider -> provider.sourcePlugin == plugin.filename }
703708
}
704709

705-
synchronized(VideoClickActionHolder.allVideoClickActions) {
706-
VideoClickActionHolder.allVideoClickActions.removeIf { action: VideoClickAction -> action.sourcePlugin == plugin.filename }
710+
VideoClickActionHolder.allVideoClickActions.withLock {
711+
VideoClickActionHolder.allVideoClickActions.removeAll { action -> action.sourcePlugin == plugin.filename }
707712
}
708713

709714
synchronized(classLoaders) {

app/src/main/java/com/lagradost/cloudstream3/syncproviders/AuthAPI.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import com.lagradost.cloudstream3.syncproviders.providers.SubSourceApi
3636
import com.lagradost.cloudstream3.ui.SyncWatchType
3737
import com.lagradost.cloudstream3.ui.library.ListSorting
3838
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
39-
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
4039
import com.lagradost.cloudstream3.utils.DataStoreHelper
4140
import com.lagradost.cloudstream3.utils.UiText
4241
import com.lagradost.cloudstream3.utils.txt

app/src/main/java/com/lagradost/cloudstream3/syncproviders/SubtitleRepo.kt

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.ErrorLoadingException
66
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleEntity
77
import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities.SubtitleSearch
88
import com.lagradost.cloudstream3.subtitles.SubtitleResource
9-
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
9+
import com.lagradost.cloudstream3.utils.Coroutines.atomicListOf
1010

1111
/** Stateless safe abstraction of SubtitleAPI */
1212
class SubtitleRepo(override val api: SubtitleAPI) : AuthRepo(api) {
@@ -24,26 +24,30 @@ class SubtitleRepo(override val api: SubtitleAPI) : AuthRepo(api) {
2424
)
2525

2626
// maybe make this a generic struct? right now there is a lot of boilerplate
27-
private val searchCache = threadSafeListOf<SavedSearchResponse>()
27+
private val searchCache = atomicListOf<SavedSearchResponse>()
2828
private var searchCacheIndex: Int = 0
29-
private val resourceCache = threadSafeListOf<SavedResourceResponse>()
29+
private val resourceCache = atomicListOf<SavedResourceResponse>()
3030
private var resourceCacheIndex: Int = 0
3131
const val CACHE_SIZE = 20
3232
}
3333

3434
@WorkerThread
3535
suspend fun resource(data: SubtitleEntity): Result<SubtitleResource> = runCatching {
36-
synchronized(resourceCache) {
36+
val cached = resourceCache.withLock {
37+
var found: SubtitleResource? = null
3738
for (item in resourceCache) {
3839
// 20 min save
3940
if (item.query == data && (unixTime - item.unixTime) < 60 * 20) {
40-
return@runCatching item.response
41+
found = item.response
42+
break
4143
}
4244
}
45+
found
4346
}
47+
if (cached != null) return@runCatching cached
4448

4549
val returnValue = api.resource(freshAuth(), data)
46-
synchronized(resourceCache) {
50+
resourceCache.withLock {
4751
val add = SavedResourceResponse(unixTime, returnValue, data)
4852
if (resourceCache.size > CACHE_SIZE) {
4953
resourceCache[resourceCacheIndex] = add // rolling cache
@@ -58,22 +62,25 @@ class SubtitleRepo(override val api: SubtitleAPI) : AuthRepo(api) {
5862
@WorkerThread
5963
suspend fun search(query: SubtitleSearch): Result<List<SubtitleEntity>> {
6064
return runCatching {
61-
synchronized(searchCache) {
65+
val cached = searchCache.withLock {
66+
var found: List<SubtitleEntity>? = null
6267
for (item in searchCache) {
6368
// 120 min save
6469
if (item.query == query && (unixTime - item.unixTime) < 60 * 120) {
65-
return@runCatching item.response
70+
found = item.response
71+
break
6672
}
6773
}
74+
found
6875
}
6976

70-
val returnValue =
71-
api.search(freshAuth(), query) ?: emptyList()
77+
if (cached != null) return@runCatching cached
78+
val returnValue = api.search(freshAuth(), query) ?: emptyList()
7279

7380
// only cache valid return values
7481
if (returnValue.isNotEmpty()) {
7582
val add = SavedSearchResponse(unixTime, returnValue, query)
76-
synchronized(searchCache) {
83+
searchCache.withLock {
7784
if (searchCache.size > CACHE_SIZE) {
7885
searchCache[searchCacheIndex] = add // rolling cache
7986
searchCacheIndex = (searchCacheIndex + 1) % CACHE_SIZE
@@ -86,4 +93,3 @@ class SubtitleRepo(override val api: SubtitleAPI) : AuthRepo(api) {
8693
}
8794
}
8895
}
89-

app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
1717
import com.lagradost.cloudstream3.mvvm.logError
1818
import com.lagradost.cloudstream3.mvvm.safeApiCall
1919
import com.lagradost.cloudstream3.newSearchResponseList
20-
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
20+
import com.lagradost.cloudstream3.utils.Coroutines.atomicListOf
2121
import com.lagradost.cloudstream3.utils.ExtractorLink
2222
import kotlinx.coroutines.CoroutineScope
2323
import kotlinx.coroutines.async
@@ -55,7 +55,7 @@ class APIRepository(val api: MainAPI) {
5555
val hash: Pair<String, String>
5656
)
5757

58-
private val cache = threadSafeListOf<SavedLoadResponse>()
58+
private val cache = atomicListOf<SavedLoadResponse>()
5959
private var cacheIndex: Int = 0
6060
const val CACHE_SIZE = 20
6161

@@ -66,9 +66,7 @@ class APIRepository(val api: MainAPI) {
6666

6767
private fun afterPluginsLoaded(forceReload: Boolean) {
6868
if (forceReload) {
69-
synchronized(cache) {
70-
cache.clear()
71-
}
69+
cache.clear()
7270
}
7371
}
7472

@@ -91,21 +89,25 @@ class APIRepository(val api: MainAPI) {
9189
val fixedUrl = api.fixUrl(url)
9290
val lookingForHash = Pair(api.name, fixedUrl)
9391

94-
synchronized(cache) {
92+
val cached = cache.withLock {
93+
var found: LoadResponse? = null
9594
for (item in cache) {
9695
// 10 min save
9796
if (item.hash == lookingForHash && (unixTime - item.unixTime) < 60 * 10) {
98-
return@withTimeout item.response
97+
found = item.response
98+
break
9999
}
100100
}
101+
found
101102
}
102103

104+
if (cached != null) return@withTimeout cached
103105
api.load(fixedUrl)?.also { response ->
104106
// Remove all blank tags as early as possible
105107
response.tags = response.tags?.filter { it.isNotBlank() }
106108
val add = SavedLoadResponse(unixTime, response, lookingForHash)
107109

108-
synchronized(cache) {
110+
cache.withLock {
109111
if (cache.size > CACHE_SIZE) {
110112
cache[cacheIndex] = add // rolling cache
111113
cacheIndex = (cacheIndex + 1) % CACHE_SIZE
@@ -215,4 +217,4 @@ class APIRepository(val api: MainAPI) {
215217
return false
216218
}
217219
}
218-
}
220+
}

app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class HomeViewModel : ViewModel() {
133133
private var currentShuffledList: List<SearchResponse> = listOf()
134134

135135
private fun autoloadRepo(): APIRepository {
136-
return APIRepository(synchronized(apis) { apis.first { it.hasMainPage } })
136+
return APIRepository(apis.withLock { apis.first { it.hasMainPage } })
137137
}
138138

139139
private val _availableWatchStatusTypes =

app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,13 @@ class LibraryFragment : BaseFragment<FragmentLibraryBinding>(
210210
syncId: SyncIdName,
211211
apiName: String? = null,
212212
) {
213-
val availableProviders = synchronized(allProviders) {
214-
allProviders.filter {
215-
it.supportedSyncNames.contains(syncId)
216-
}.map { it.name } +
217-
// Add the api if it exists
218-
(APIHolder.getApiFromNameNull(apiName)?.let { listOf(it.name) }
219-
?: emptyList())
220-
}
213+
val availableProviders = allProviders.filter {
214+
it.supportedSyncNames.contains(syncId)
215+
}.map { it.name } +
216+
// Add the api if it exists
217+
(APIHolder.getApiFromNameNull(apiName)?.let { listOf(it.name) }
218+
?: emptyList())
219+
221220
val baseOptions = listOf(
222221
LibraryOpenerType.Default,
223222
LibraryOpenerType.None,

app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,14 +1685,13 @@ class ResultViewModel2 : ViewModel() {
16851685
}
16861686

16871687
val realRecommendations = ArrayList<SearchResponse>()
1688-
val apiNames = synchronized(apis) {
1689-
apis.filter {
1690-
it.name.contains("gogoanime", true) ||
1691-
it.name.contains("9anime", true)
1692-
}.map {
1693-
it.name
1694-
}
1688+
val apiNames = apis.filter {
1689+
it.name.contains("gogoanime", true) ||
1690+
it.name.contains("9anime", true)
1691+
}.map {
1692+
it.name
16951693
}
1694+
16961695
meta.recommendations?.forEach { rec ->
16971696
apiNames.forEach { name ->
16981697
realRecommendations.add(rec.copy(apiName = name))

0 commit comments

Comments
 (0)