-
Notifications
You must be signed in to change notification settings - Fork 308
occur crash when switch DB #1202
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
Comments
Thanks for reporting! However, as is I can't help much. It is possible that your code does not properly close BoxStore before creating a new instance. But I can't really tell from what you have shared, for example what happens when To give you better support, please:
Note: I labeled this issue with "more info required" so it will auto-close in a few days if there are no follow-up comments. |
@Module
@InstallIn(SingletonComponent::class)
object ObjectBoxModule {
@Provides
@Singleton
@Impermanence
fun provideImpermanenceObjectBox(@ApplicationContext context: Context): BoxStore {
BoxStore.deleteAllFiles(context, "impermanenceDB")
return MyObjectBox.builder()
.androidContext(context.applicationContext)
.name("impermanenceDB")
.build()
}
@Provides
@Singleton
@TestMode
fun provideTestModeObjectBox(@ApplicationContext context: Context): BoxStore {
BoxStore.deleteAllFiles(context, "testModeDB")
return MyObjectBox.builder()
.androidContext(context.applicationContext)
.name("testModeDB")
.build()
}
}
|
Thanks for the details! I could symbolize the backtrace. The error is According to the sigaction manual And the second to last line of the backtrace is Based on that: does your code make sure, that all transactions (like queries) are finished and references to all ObjectBox objects (like For our reference, this is internal issue |
Thank you for your kind reply, I'll try to solve it, and if it doesn't work, I'll ask you again. |
Here is another issue. please check this issue. @OptIn(ExperimentalCoroutinesApi::class)
class DeviceBoxImpl @Inject constructor(
@Impermanence private val impermanenceDetectorBox: Box<DetectorEntity>,
@Impermanence private val impermanenceRepeaterBox: Box<RepeaterEntity>,
@Impermanence private val impermanenceInputBox: Box<RepeaterInputEntity>,
@Impermanence private val impermanenceOutputBox: Box<RepeaterOutputEntity>,
@TestMode private val testModeDetectorBox: Box<DetectorEntity>,
@TestMode private val testModeRepeaterBox: Box<RepeaterEntity>,
@TestMode private val testModeInputBox: Box<RepeaterInputEntity>,
@TestMode private val testModeOutputBox: Box<RepeaterOutputEntity>,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
private val detectorRecordBox: Box<DetectorRecordEntity>,
private val testModeManager: TestModeManager
) : DeviceBox {
private val detectorBox: Box<DetectorEntity>
get() = if (testModeManager.isTestMode) testModeDetectorBox else impermanenceDetectorBox
private val repeaterBox: Box<RepeaterEntity>
get() = if (testModeManager.isTestMode) testModeRepeaterBox else impermanenceRepeaterBox
private val inputBox: Box<RepeaterInputEntity>
get() = if (testModeManager.isTestMode) testModeInputBox else impermanenceInputBox
private val outputBox: Box<RepeaterOutputEntity>
get() = if (testModeManager.isTestMode) testModeOutputBox else impermanenceOutputBox
override suspend fun addOutputInstance(instance: RepeaterOutputEntity) =
withContext(ioDispatcher) {
outputBox.put(instance)
outputBox.closeThreadResources()
}
override suspend fun addOutputInstances(instances: List<RepeaterOutputEntity>) =
withContext(ioDispatcher) {
outputBox.put(instances)
outputBox.closeThreadResources()
}
override fun observeDeviceInputInstancesByDetectorFireOn(): Flow<List<DeviceInputEntity>> {
val query = detectorBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.fire != newItem.fire || oldItem.test != newItem.test
}
}
}
}
.map { inputList ->
inputList.filter { it.fire || it.test }
.map { DeviceInputEntity.Detector(it) }
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override fun observeDeviceInputInstancesByInputSignalOn(): Flow<List<DeviceInputEntity>> {
val query = inputBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.signal != newItem.signal || oldItem.test != newItem.test
}
}
}
}
.map { inputList ->
inputList.filter { it.signal || it.test }
.map { DeviceInputEntity.RepeaterInput(it) }
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override fun observeOutputFullCompositeKeysByDetectorFireOff(): Flow<Array<String>> {
val query = detectorBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.fire != newItem.fire || oldItem.test != newItem.test
}
}
}
}
.map { inputList ->
inputList.filter { !it.fire && !it.test }
.flatMap { it.outputList }
.toTypedArray()
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override fun observeOutputFullCompositeKeysByInputSignalOff(): Flow<Array<String>> {
val query = inputBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.signal != newItem.signal || oldItem.test != newItem.test
}
}
}
}
.map { inputList ->
inputList.filter { !it.signal && !it.test }
.flatMap { it.outputList }
.toTypedArray()
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override fun observeOutputInstancesByTestOn(): Flow<List<RepeaterOutputEntity>> {
val query = outputBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.test != newItem.test
}
}
}
}
.map { outputList ->
outputList.filter { it.test }
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override fun observeOutputFullCompositeKeysByTestOff(): Flow<Array<String>> {
val query = outputBox.query().build()
return query.subscribe().toFlow()
.zipWithPrevious()
.map { (oldList, newList) ->
if (oldList == null) {
emptyList()
} else {
val oldMap = oldList.associateBy { it.fullCompositeKey }
newList.filter { newItem ->
val oldItem = oldMap[newItem.fullCompositeKey]
if (oldItem == null) {
false
} else {
oldItem.test != newItem.test
}
}
}
}
.map { outputList ->
outputList.filter { !it.test }
.map { it.fullCompositeKey }
.toTypedArray()
}
.filter { it.isNotEmpty() }
.flowOn(ioDispatcher)
.onCompletion { query.close() }
}
override suspend fun getOutputInstancesByFullCompositeKeys(fullCompositeKeys: Array<String>): Map<String, RepeaterOutputEntity> =
withContext(ioDispatcher) {
val query = outputBox.query().inValues(
RepeaterOutputEntity_.fullCompositeKey,
fullCompositeKeys,
QueryBuilder.StringOrder.CASE_INSENSITIVE
).build()
query.use { query.find().distinctBy { it.fullCompositeKey }.associateBy { it.fullCompositeKey } }
}
override suspend fun getOutputInstanceByFullCompositeKey(fullCompositeKey: String): RepeaterOutputEntity? =
withContext(ioDispatcher) {
val query =
outputBox.query(RepeaterOutputEntity_.fullCompositeKey.equal(fullCompositeKey))
.build()
query.use { query.findUnique() }
}
override suspend fun areAllInputSignalsTrueByFullCompositeKeys(fullCompositeKeys: Array<String>): Boolean =
withContext(ioDispatcher) {
val query =
inputBox.query()
.inValues(RepeaterInputEntity_.fullCompositeKey, fullCompositeKeys, QueryBuilder.StringOrder.CASE_INSENSITIVE)
.build()
query.use { query.find().all { it.signal || it.test } }
}
private fun <T> Flow<T>.zipWithPrevious(): Flow<Pair<T?, T>> = flow {
var previous: T? = null
collect { current ->
emit(previous to current)
previous = current
}
}
}
class CrossCircuitBoxImpl @Inject constructor(
private val crossCircuitBox: Box<CrossCircuitEntity>,
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
): CrossCircuitBox {
override suspend fun getCrossCircuitByOutputFullCompositeKey(fullCompositeKey: String): List<CrossCircuitEntity> = withContext(ioDispatcher) {
val query = crossCircuitBox.query(CrossCircuitEntity_.outputList.containsElement(fullCompositeKey)).build()
query.use { query.find() }
}
override suspend fun getOutputFullCompositeKeysByInputFullCompositeKey(fullCompositeKey: String): List<String> = withContext(ioDispatcher) {
val query = crossCircuitBox.query(CrossCircuitEntity_.inputList.containsElement(fullCompositeKey)).build()
query.use { query.find().flatMap { it.outputList } }
}
}
class DeviceRepositoryImpl @Inject constructor(
private val deviceBox: DeviceBox,
private val crossCircuitBox: CrossCircuitBox,
@DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
) : DeviceRepository {
private val scope = CoroutineScope(SupervisorJob() + defaultDispatcher)
private var commJob: Job? = null
override fun makeOutputRequest() {
commJob = scope.launch {
launch {
merge(
deviceBox.observeDeviceInputInstancesByInputSignalOn(),
deviceBox.observeDeviceInputInstancesByDetectorFireOn()
)
.collect { list ->
val inputList = list.map { it.fullCompositeKey }
val outputList: MutableList<String> = mutableListOf()
outputList.addAll(list.flatMap { it.outputList })
inputList.forEach { fullCompositeKey ->
val crossCircuitOutputList = crossCircuitBox.getOutputFullCompositeKeysByInputFullCompositeKey(fullCompositeKey)
if (crossCircuitOutputList.isNotEmpty()) {
outputList.addAll(crossCircuitOutputList)
}
}
val outputEntityMap = deviceBox.getOutputInstancesByFullCompositeKeys(outputList.toTypedArray())
outputList.forEach { fullCompositeKey ->
val outputEntity = outputEntityMap[fullCompositeKey]
if(outputEntity != null) {
val crossCircuitList = crossCircuitBox.getCrossCircuitByOutputFullCompositeKey(fullCompositeKey)
if(crossCircuitList.isNotEmpty()) {
crossCircuitList.forEach { crossCircuit ->
if(deviceBox.areAllInputSignalsTrueByFullCompositeKeys(crossCircuit.inputList.toTypedArray())) {
if (!outputEntity.signal) {
outputEntity.request = true
} else {
outputEntity.test = false
}
outputEntity.inputSignalOnCount++
}
}
} else {
if (!outputEntity.signal) {
outputEntity.request = true
} else {
outputEntity.test = false
}
outputEntity.inputSignalOnCount++
}
}
}
deviceBox.addOutputInstances(outputEntityMap.values.toList())
}
}
launch {
deviceBox.observeOutputInstancesByTestOn().collect { list ->
list.forEach { outputEntity ->
if (!outputEntity.signal) outputEntity.request = true
outputEntity.inputSignalOnCount++
}
deviceBox.addOutputInstances(list)
}
}
launch {
merge(
deviceBox.observeOutputFullCompositeKeysByInputSignalOff(),
deviceBox.observeOutputFullCompositeKeysByTestOff(),
deviceBox.observeOutputFullCompositeKeysByDetectorFireOff()
)
.collect { list ->
val outputEntityMap = deviceBox.getOutputInstancesByFullCompositeKeys(list)
list.forEach { fullCompositeKey ->
val outputEntity = outputEntityMap[fullCompositeKey]
if (outputEntity != null) {
if (outputEntity.inputSignalOnCount > 0) {
outputEntity.inputSignalOnCount--
}
if (outputEntity.signal && outputEntity.inputSignalOnCount == 0) {
outputEntity.request = true
}
}
}
deviceBox.addOutputInstances(outputEntityMap.values.toList())
}
}
}
}
override suspend fun stop() {
commJob?.cancelAndJoin()
commJob = null
}
} This is a stopable coroutine code(DeviceRepository). When it actually stops, the above error occurs. When I read or put on objectbox, should I not |
This comment has been minimized.
This comment has been minimized.
Actually, the above should not be possible if @udyr-woo In your code, is it possible that |
Thank you for answer. My code is working from scratch now, so I won't be able to test it right now, but I'll try not to use closeThreadResources(). And BoxStore doesn't close until the app shuts down. I just shut down Coroutin. |
Sorry, I was not clear enough. I made a mistake. Using This crash should only be able to occur when the |
This is an error from a different business logic. The logic of closing the boxstore still doesn't exist. It's also hard to re-enact. What is causing this error? |
As I previously said, this can also happen if the For testing, your code could maybe call |
Uh oh!
There was an error while loading. Please reload this page.
Is there an existing issue?
Build info
Steps to reproduce
I used normal db and test db(both are objectBox).
When changing isTestMode of a single-toned testMode Manager, there is a logic that the dataSource changes accordingly. Before changing, the work related to the db is terminated, but for some reason, a crash occurs.
Expected behavior
Don't occur crash
Actual behavior
Occur crash
Code
Code
Log
Logs
The text was updated successfully, but these errors were encountered: